mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2026-01-13 23:17:10 +03:00
Remove wrong copy
This commit is contained in:
@@ -1,791 +0,0 @@
|
||||
22 Nov 2010 - 2.5.13-dev3
|
||||
-------------------------
|
||||
|
||||
* Add SecReadStateLimit to limit the number of concurrent threads in BUSY connections per ip address
|
||||
|
||||
* Fixed redirect action was not expanding macros in chained rules
|
||||
|
||||
04 Nov 2010 - 2.5.13-dev2
|
||||
-------------------------
|
||||
|
||||
* Fixed Geo lookup concurrent connections bug
|
||||
|
||||
* Fixed Skip/SkipAfter chain bug
|
||||
|
||||
* Added new setvar Lua API to be used into Lua scripts
|
||||
|
||||
* Added PCRE messages indicates each rule that exceed match limits
|
||||
|
||||
* Added new Base64 transformation function called base64DecodeEx, which
|
||||
can decode base64 data skipping special characters.
|
||||
|
||||
|
||||
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:<id> 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 <id> 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.
|
||||
|
||||
* 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. <Location>).
|
||||
|
||||
* 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.
|
||||
|
||||
|
||||
16 Oct 2006 - 2.0.1
|
||||
-------------------
|
||||
|
||||
* Rules that used operator negation did not work. Fixed.
|
||||
|
||||
* Fixed bug that prevented invalid regular expressions from being reported.
|
||||
|
||||
|
||||
16 Oct 2006 - 2.0.0
|
||||
-------------------
|
||||
|
||||
* First stable 2.x release.
|
||||
@@ -1,281 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
|
||||
MODSECURITY LICENSING EXCEPTION
|
||||
===============================
|
||||
|
||||
Version 1.0, 29 July 2008
|
||||
|
||||
As a special exception ("Exception") to the terms and conditions of version 2
|
||||
of the GPL, Trustwave Holdings, Inc. hereby grants you the rights described
|
||||
below, provided you agree to the terms and conditions in this Exception,
|
||||
including its obligations and restrictions on use.
|
||||
|
||||
|
||||
Exception Intent
|
||||
================
|
||||
|
||||
We want specified Free/Libre and Open Source Software ("FLOSS") programs to be
|
||||
able to use ModSecurity (the "Program") despite the fact that not all FLOSS
|
||||
licenses are compatible with version 2 of the GNU General Public License (the
|
||||
"GPLv2").
|
||||
|
||||
|
||||
Legal Terms and Conditions
|
||||
==========================
|
||||
|
||||
You are free to distribute a Derivative Work that is formed entirely from the
|
||||
Program and one or more works (each, a "FLOSS Work") licensed under one or
|
||||
more of the licenses listed below in section 1, as long as all of the
|
||||
following conditions are met:
|
||||
|
||||
1. You obey the GPLv2 in all respects for the Program and the Derivative
|
||||
Work, except for identifiable sections of the Derivative Work which are
|
||||
|
||||
1. not derived from the Program, and
|
||||
|
||||
2. are not designed to interact with the Program, and
|
||||
|
||||
3. which can reasonably be considered independent and separate works in
|
||||
themselves.
|
||||
|
||||
2. All such identifiable sections of the Derivative Work are
|
||||
|
||||
1. distributed subject to one of the FLOSS licenses listed below, and
|
||||
|
||||
2. the object code or executable form of those sections are accompanied
|
||||
by the complete corresponding machine-readable source code for those
|
||||
sections on the same medium and under the same FLOSS license as the
|
||||
corresponding object code or executable forms of those sections.
|
||||
|
||||
3. Any works which are aggregated with the Program or with a Derivative Work
|
||||
on a volume of a storage or distribution medium in accordance with the
|
||||
GPLv2, can reasonably be considered independent and separate works in
|
||||
themselves which are not derivatives of either the Program, a Derivative
|
||||
Work or a FLOSS Work, and are not designed to interact with the Program.
|
||||
|
||||
If the above conditions are not met, then the Program may only be copied,
|
||||
modified, distributed or used under the terms and conditions of the GPLv2
|
||||
or another valid licensing option from Trustwave Holdings, Inc.
|
||||
|
||||
|
||||
FLOSS License List
|
||||
==================
|
||||
|
||||
License name Version(s)/Copyright Date
|
||||
-----------------------------------------------------------------------
|
||||
Academic Free License 2.0
|
||||
Apache Software License 1.0/1.1/2.0
|
||||
Apple Public Source License 2.0
|
||||
Artistic license From Perl 5.8.0
|
||||
BSD license "July 22 1999"
|
||||
Common Development and Distribution License (CDDL) 1.0
|
||||
Common Public License 1.0
|
||||
Eclipse Public License 1.0
|
||||
GNU Library or "Lesser" General Public License (LGPL) 2.0/2.1/3.0
|
||||
Jabber Open Source License 1.0
|
||||
MIT License (As listed in file MIT-License.txt) -
|
||||
Mozilla Public License (MPL) 1.0/1.1
|
||||
Open Software License 2.0
|
||||
OpenSSL license (with original SSLeay license) "2003" ("1998")
|
||||
PHP License 3.0
|
||||
Python license (CNRI Python License) -
|
||||
Python Software Foundation License 2.1.1
|
||||
Sleepycat License "1999"
|
||||
University of Illinois/NCSA Open Source License -
|
||||
W3C License "2001"
|
||||
X11 License "2001"
|
||||
Zlib/libpng License -
|
||||
Zope Public License 2.0
|
||||
|
||||
Due to the many variants of some of the above licenses, we require that for
|
||||
any version of the listed FLOSS licenses to qualify under this exception, it
|
||||
must follow the 2003 version of the Free Software Foundation's Free Software
|
||||
Definition (http://www.gnu.org/philosophy/free-sw.html) or version 1.9 of the
|
||||
Open Source Definition by the Open Source Initiative
|
||||
(http://www.opensource.org/docs/definition.php).
|
||||
|
||||
|
||||
Definitions
|
||||
===========
|
||||
|
||||
1. Terms used, but not defined, herein shall have the meaning provided in the
|
||||
version 2 of the GPL.
|
||||
|
||||
2. Derivative Work means a derivative work under copyright law.
|
||||
|
||||
|
||||
Applicability
|
||||
=============
|
||||
|
||||
This Exception applies to all Programs that contain a notice placed by Trustwave Holdings
|
||||
Security, Inc. saying that the Program may be distributed under the terms of
|
||||
this Exception. If you create or distribute a work which is a Derivative Work
|
||||
of both the Program and any other work licensed under the GPL, then this FLOSS
|
||||
Exception is not available for that work; thus, you must remove the FLOSS
|
||||
Exception notice from that work and comply with the GPL in all respects,
|
||||
including by retaining all GPL notices.
|
||||
|
||||
You may choose to redistribute a copy of the Program exclusively under the
|
||||
terms of the GPLv2 by removing the Exception notice from that copy of the
|
||||
Program, provided that the copy has never been modified by you or any third
|
||||
party.
|
||||
|
||||
|
||||
Appendix A. Qualified Libraries and Packages
|
||||
============================================
|
||||
|
||||
The following is a non-exhaustive list of libraries and packages which are
|
||||
covered by the Exception when they are licensed under one or more of the
|
||||
licenses listed above. Please note that this appendix is merely provided as
|
||||
an additional service to specific FLOSS projects who wish to simplify
|
||||
licensing information for their users. Compliance with one of the licenses
|
||||
noted under the "FLOSS license list" section remains a prerequisite.
|
||||
|
||||
Package name Qualifying License and Version
|
||||
-----------------------------------------------------------------
|
||||
Apache HTTP Server Apache Software License 2.0
|
||||
Apache Portable Runtime (APR) Apache Software License 2.0
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
|
||||
ModSecurity for Apache is an open source product, released under terms of
|
||||
the General Public Licence, Version 2 (GPLv2). Please refer to the
|
||||
file LICENSE, which contains the complete text of the licence.
|
||||
|
||||
There are special exceptions to the terms and conditions of the GPL
|
||||
as it is applied to this software. View the full text of the exception in
|
||||
file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
distribution.
|
||||
|
||||
|
||||
DOCUMENTATION
|
||||
|
||||
Please refer to the documentation folder (/doc) for
|
||||
the reference manual.
|
||||
@@ -1,181 +0,0 @@
|
||||
============================================================
|
||||
Build notes for Windows from Tom Donovan
|
||||
============================================================
|
||||
These are the raw build notes from Tom Donovan for building
|
||||
ModSecurity 2.5.13 with Apache httpd 2.2.14 on Windows. Some
|
||||
day these should be incorporated into the official docs, but
|
||||
there has not yet been time, so they are included here in
|
||||
their raw format for now.
|
||||
============================================================
|
||||
|
||||
I build Apache 2.2.14 from source in C:\work\httpd-2.2.14
|
||||
I have a VC9 build of Apache 2.2.14 installed in C:\Apache2214
|
||||
|
||||
My PATH includes VC9 and CMAKE 2.6
|
||||
|
||||
BEFORE BUILDING - if OpenSSL and Zlib support is desired in LIBXML2 and CURL
|
||||
|
||||
REM #### set an env variable to my Apache build directory
|
||||
SET HTTPD_BUILD=C:\work\httpd-2.2.14
|
||||
|
||||
REM #### 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
|
||||
|
||||
REM #### ensure that CURL doesn't use the static zlib library: zlib.lib. Force it to use zdll.lib instead, which points to zlib1.dll
|
||||
IF EXIST %HTTPD_BUILD%\srclib\zlib\zlib.lib DEL %HTTPD_BUILD%\srclib\zlib\zlib.lib
|
||||
|
||||
BUILD PCRE-7.9
|
||||
|
||||
Downloaded pcre-7.9.tar.gz from ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
|
||||
untar'd into C:\work\ creating C:\work\pcre-7.9
|
||||
|
||||
CD C:\work\pcre-7.9
|
||||
CMAKE -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=True
|
||||
NMAKE
|
||||
|
||||
BUILD LIBXML2-2.7.6
|
||||
|
||||
Downloaded libxml2-2.7.6.tar.gz from ftp://xmlsoft.org/libxml2/
|
||||
untar'd into C:\work\ creating C:\work\libxml2-2.7.6
|
||||
|
||||
CD C:\work\libxml2-2.7.6\win32
|
||||
CSCRIPT configure.js iconv=no vcmanifest=yes zlib=yes
|
||||
NMAKE -f Makefile.msvc
|
||||
|
||||
BUILD LUA-5.1.4
|
||||
|
||||
Downloaded lua-5.1.4.tar.gz from http://www.lua.org/ftp/
|
||||
untar'd into C:\work\ creating C:\work\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.20.0
|
||||
Downloaded curl-7.20.0.tar.gz from http://curl.haxx.se/download.html
|
||||
untar'd into C:\work\ creating C:\work\curl-7.20.0
|
||||
|
||||
CD C:\work\curl-7.20.0
|
||||
|
||||
*** Fixed Bug: https://sourceforge.net/tracker/?func=detail&aid=2951269&group_id=976&atid=100976 ***
|
||||
Edited the file include\curl\curlbuild.h.cmake near line 160 - put double-quotes around all CURL_FORMAT* values.
|
||||
e.g. change: ${CURL_FORMAT_CURL_OFF_T} to: "${CURL_FORMAT_CURL_OFF_T}"
|
||||
|
||||
/* curl_off_t formatting string directive without "%" conversion specifier. */
|
||||
#cmakedefine CURL_FORMAT_CURL_OFF_T "${CURL_FORMAT_CURL_OFF_T}"
|
||||
|
||||
/* unsigned curl_off_t formatting string without "%" conversion specifier. */
|
||||
#cmakedefine CURL_FORMAT_CURL_OFF_TU "${CURL_FORMAT_CURL_OFF_TU}"
|
||||
|
||||
/* curl_off_t formatting string directive with "%" conversion specifier. */
|
||||
#cmakedefine CURL_FORMAT_OFF_T "${CURL_FORMAT_OFF_T}"
|
||||
|
||||
CMAKE -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=True -DCURL_ZLIB=True
|
||||
|
||||
NMAKE
|
||||
|
||||
BUILD MOD_SECURITY-2.5.13
|
||||
|
||||
Edited the top of C:\work\mod_security-2.5.13\apache2\Makefile.win and set my local paths
|
||||
(note that pcre.lib is not in $(PCRE)\LibR as it is in the original Makefile.win )
|
||||
|
||||
# Path to Apache httpd installation
|
||||
BASE = C:\Apache2214
|
||||
|
||||
# Paths to required libraries
|
||||
LIBXML2 = C:\work\libxml2-2.7.6
|
||||
LUA = C:\work\lua-5.1.4\src
|
||||
PCRE = C:\work\pcre-7.9
|
||||
|
||||
# Linking libraries
|
||||
LIBS = $(BASE)\lib\libhttpd.lib \
|
||||
$(BASE)\lib\libapr-1.lib \
|
||||
$(BASE)\lib\libaprutil-1.lib \
|
||||
$(PCRE)\pcre.lib \
|
||||
$(LIBXML2)\win32\bin.msvc\libxml2.lib \
|
||||
$(LUA)\lua5.1.lib \
|
||||
wsock32.lib
|
||||
|
||||
CD C:\work\mod_security-2.5.13\apache2
|
||||
NMAKE -f Makefile.win
|
||||
|
||||
BUILD MOD_SECURITY-2.5.13 MLOGC program
|
||||
|
||||
Edited the top of C:\work\mod_security-2.5.13\apache2\mlogc-src\Makefile.win and set my local paths
|
||||
|
||||
# Path to Apache httpd installation
|
||||
BASE = C:\Apache2214
|
||||
|
||||
# Paths to required libraries
|
||||
PCRE = C:\work\pcre-7.9
|
||||
CURL = C:\work\curl-7.20.0
|
||||
|
||||
# Linking libraries
|
||||
LIBS = $(BASE)\lib\libapr-1.lib \
|
||||
$(BASE)\lib\libaprutil-1.lib \
|
||||
$(PCRE)\pcre.lib \
|
||||
$(CURL)\libcurl_imp.lib \
|
||||
wsock32.lib
|
||||
|
||||
|
||||
CD C:\work\mod_security-2.5.13\apache2\mlogc-src
|
||||
NMAKE -f Makefile.win
|
||||
|
||||
INSTALL AND RUN
|
||||
|
||||
Copied these five files to C:\Apache2214\bin:
|
||||
C:\work\pcre-7.9\pcre.dll
|
||||
C:\work\lua-5.1.4\src\lua5.1.dll
|
||||
C:\work\libxml2-2.7.6\win32\bin.msvc\libxml2.dll
|
||||
C:\work\curl-7.20.0\libcurl.dll
|
||||
C:\work\mod_security-2.5.13\apache2\mlogc-src\mlogc.exe
|
||||
|
||||
Copied this one file to C:\Apache2214\modules:
|
||||
|
||||
C:\work\mod_security-2.5.13\apache2\mod_security2.so
|
||||
|
||||
You could also copy C:\work\curl-7.20.0\\curl.exe to C:\Apache2214\bin, if you want to use the cURL command-line.
|
||||
|
||||
Downloaded the core rules from http://sourceforge.net/projects/mod-security/files/modsecurity-crs/0-CURRENT/
|
||||
and unzipped them in C:\Apache2214\conf\modsecurity_crs
|
||||
|
||||
Added this to my conf\httpd.conf:
|
||||
|
||||
LoadModule unique_id_module modules/mod_unique_id.so
|
||||
LoadModule security2_module modules/mod_security2.so
|
||||
<IfModule security2_module>
|
||||
Include conf/modsecurity_crs/*.conf
|
||||
Include conf/modsecurity_crs/base_rules/*.conf
|
||||
SecDataDir logs
|
||||
SecAuditEngine RelevantOnly
|
||||
SecAuditLogRelevantStatus "^(?:5|4\d[^4])"
|
||||
SecAuditLogType Concurrent
|
||||
SecAuditLogParts ABCDEFGHZ
|
||||
SecAuditLogStorageDir logs/data/
|
||||
SecAuditLog "|bin/mlogc.exe"
|
||||
</IfModule>
|
||||
|
||||
My conf\mlogc.conf has this:
|
||||
CollectorRoot "C:/Apache2214/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
|
||||
|
||||
|
||||
Mod_security appears to work OK with the "ModSecurity Community Console".
|
||||
@@ -1,127 +0,0 @@
|
||||
# Makefile for ModSecurity
|
||||
|
||||
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 acmp msc_lua msc_release
|
||||
|
||||
MSC_TEST = 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 msc_geo acmp msc_lua msc_release
|
||||
|
||||
MOD_SECURITY2_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 acmp.h utf8tables.h msc_lua.h msc_release.h
|
||||
|
||||
CC = @APXS_CC@
|
||||
LIBTOOL = @APXS_LIBTOOL@
|
||||
PERL = @PERL@
|
||||
EXTRA_CFLAGS = @EXTRA_CFLAGS@
|
||||
MODSEC_EXTRA_CFLAGS = @MODSEC_EXTRA_CFLAGS@
|
||||
|
||||
### Note: must be in APXS format: -Wc,-flag
|
||||
APXS_EXTRA_CFLAGS = @APXS_EXTRA_CFLAGS@
|
||||
MODSEC_APXS_EXTRA_CFLAGS = @MODSEC_APXS_EXTRA_CFLAGS@
|
||||
|
||||
APXS = @APXS@
|
||||
APXS_WRAPPER = @APXS_WRAPPER@
|
||||
APXS_INCLUDEDIR = @APXS_INCLUDEDIR@
|
||||
APXS_INCLUDES = @APXS_INCLUDES@
|
||||
APXS_CFLAGS = @APXS_CFLAGS@
|
||||
APXS_LDFLAGS = @APXS_LDFLAGS@
|
||||
APXS_LIBS = @APXS_LIBS@
|
||||
|
||||
PCRE_CFLAGS = @PCRE_CFLAGS@
|
||||
PCRE_LIBS = @PCRE_LIBS@
|
||||
|
||||
LUA_CFLAGS = @LUA_CFLAGS@
|
||||
LUA_LIBS = @LUA_LIBS@
|
||||
|
||||
LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
|
||||
LIBXML2_LIBS = @LIBXML2_LIBS@
|
||||
|
||||
APR_CFLAGS = @APR_CFLAGS@
|
||||
APR_LDFLAGS = @APR_LDFLAGS@
|
||||
APR_LIBS = @APR_LIBS@
|
||||
APR_LINK_LD = @APR_LINK_LD@
|
||||
|
||||
APU_CFLAGS = @APU_CFLAGS@
|
||||
APU_LDFLAGS = @APU_LDFLAGS@
|
||||
APU_LIBS = @APU_LIBS@
|
||||
APU_LINK_LD = @APU_LINK_LD@
|
||||
|
||||
CPPFLAGS = @CPPFLAGS@ $(PCRE_CFLAGS) $(LIBXML2_CFLAGS) $(LUA_CFLAGS)
|
||||
LIBS = @LIBS@ $(PCRE_LIBS) $(LIBXML2_LIBS) $(LUA_LIBS)
|
||||
LDFLAGS = @LDFLAGS@
|
||||
CFLAGS = @CFLAGS@
|
||||
|
||||
COMPILE_APACHE_MOD = $(APXS_WRAPPER) -c $(CPPFLAGS) $(LDFLAGS) $(LIBS)
|
||||
|
||||
INSTALL_MOD_SHARED = $(APXS_WRAPPER) -i
|
||||
|
||||
all: mod_security2.la
|
||||
|
||||
install: install-mods
|
||||
|
||||
clean-extras:
|
||||
@for dir in mlogc-src; do \
|
||||
if test -d $$dir; then \
|
||||
(cd $$dir && $(MAKE) clean); \
|
||||
fi; \
|
||||
done
|
||||
@rm -rf ../tools/mlogc ../tools/mlogc-batch-load.pl
|
||||
|
||||
clean: clean-extras
|
||||
@rm -rf *.la *.lo *.loT *.o *.slo .libs msc_test msc-test-debug.log
|
||||
|
||||
distclean: clean
|
||||
@rm -rf Makefile mlogc-src/Makefile mlogc-src/mlogc-batch-load.pl ../tools/*.pl t/run-unit-tests.pl t/run-regression-tests.pl t/gen_rx-pm.pl t/csv_rx-pm.pl t/run-tests.pl t/regression/server_root/conf/httpd.conf t/regression/server_root/conf/*.t_*.conf t/regression/server_root/tmp/* t/regression/server_root/logs/*.log t/regression/server_root/logs/audit/* t/regression/server_root/upload/* t/regression/server_root/data/* config config.log config.status build/apxs-wrapper
|
||||
|
||||
maintainer-clean: distclean
|
||||
@rm -rf config config.log config.status configure mod_security2_config.h autoscan.log configure.scan build/libtool.m4 build/config.guess build/config.sub build/ltmain.sh
|
||||
|
||||
install-mods: mod_security2.la
|
||||
$(INSTALL_MOD_SHARED) mod_security2.la
|
||||
|
||||
${MOD_SECURITY2:=.slo}: $(MOD_SECURITY2_H)
|
||||
${MOD_SECURITY2:=.lo}: $(MOD_SECURITY2_H)
|
||||
${MOD_SECURITY2:=.o}: $(MOD_SECURITY2_H})
|
||||
|
||||
mod_security2.la: $(MOD_SECURITY2_H) *.c
|
||||
@src=""; \
|
||||
for f in $(MOD_SECURITY2); do \
|
||||
src="$$src $$f.c"; \
|
||||
done; \
|
||||
rm -f msc_test msc_test.o msc_test.lo msc_test.slo; \
|
||||
$(COMPILE_APACHE_MOD) $(APXS_EXTRA_CFLAGS) $(MODSEC_APXS_EXTRA_CFLAGS) $$src
|
||||
|
||||
### MLogC
|
||||
mlogc:
|
||||
@(cd mlogc-src && $(MAKE) mlogc) \
|
||||
&& cp -p mlogc-src/mlogc ../tools \
|
||||
&& cp -p mlogc-src/mlogc-batch-load.pl ../tools \
|
||||
&& echo \
|
||||
&& echo "Successfully built \"mlogc\" in ../tools." \
|
||||
&& echo "See: mlogc-src/INSTALL" \
|
||||
&& echo
|
||||
|
||||
### Experimental Test Framework (*NIX only right now)
|
||||
msc_test.lo: msc_test.c
|
||||
$(LIBTOOL) --mode=compile $(CC) $(APXS_INCLUDES) $(APXS_CFLAGS) $(CPPFLAGS) $(APR_CFLAGS) $(APU_CFLAGS) -o msc_test.lo -c msc_test.c
|
||||
|
||||
msc_test: $(TESTOBJS) $(MOD_SECURITY2_H}) msc_test.lo
|
||||
objs=""; \
|
||||
for f in $(MSC_TEST); do \
|
||||
objs="$$objs $$f.lo"; \
|
||||
done; \
|
||||
$(LIBTOOL) --mode=link $(CC) $$objs -o msc_test msc_test.lo $(LDFLAGS) $(LIBS) $(APR_LINK_LD) $(APU_LINK_LD)
|
||||
|
||||
test: t/run-unit-tests.pl msc_test
|
||||
@rm -f msc-test-debug.log; \
|
||||
$(PERL) t/run-unit-tests.pl
|
||||
|
||||
test-regression: t/run-regression-tests.pl
|
||||
@$(PERL) t/run-regression-tests.pl
|
||||
|
||||
.PHONY: all install clean-extras clean maintainer-clean distclean install-mods test test-regression
|
||||
@@ -1,73 +0,0 @@
|
||||
###########################################################################
|
||||
### You Will need to modify the following variables for your system
|
||||
###########################################################################
|
||||
###########################################################################
|
||||
|
||||
# Path to Apache httpd installation
|
||||
BASE = C:\Apache2
|
||||
|
||||
# Paths to required libraries
|
||||
LIBXML2 = C:\work\libxml2-2.6.31
|
||||
LUA = C:\work\lua-5.1.3
|
||||
PCRE = C:\work\httpd-2.2.8\srclib\pcre
|
||||
|
||||
# Linking libraries
|
||||
LIBS = $(BASE)\lib\libhttpd.lib \
|
||||
$(BASE)\lib\libapr-1.lib \
|
||||
$(BASE)\lib\libaprutil-1.lib \
|
||||
$(PCRE)\LibR\pcre.lib \
|
||||
$(LIBXML2)\win32\bin.msvc\libxml2.lib \
|
||||
$(LUA)\lua5.1.lib \
|
||||
wsock32.lib
|
||||
|
||||
###########################################################################
|
||||
###########################################################################
|
||||
|
||||
CC = cL
|
||||
|
||||
MT = mt
|
||||
|
||||
DEFS = /nologo /O2 /LD /W3 /wd4244 -DWIN32 -DWINNT -Dinline=APR_INLINE
|
||||
|
||||
DLL = mod_security2.so
|
||||
|
||||
INCLUDES = -I. \
|
||||
-I$(PCRE)\include -I$(PCRE) \
|
||||
-I$(LIBXML2)\include \
|
||||
-I$(LUA)\include -I$(LUA) \
|
||||
-I$(BASE)\include
|
||||
|
||||
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 pdf_protect.obj msc_geo.obj acmp.obj msc_lua.obj \
|
||||
msc_release.obj
|
||||
|
||||
all: $(DLL)
|
||||
|
||||
dll: $(DLL)
|
||||
|
||||
mod_security2_config.h: mod_security2_config.hw
|
||||
@echo off
|
||||
type mod_security2_config.hw > mod_security2_config.h
|
||||
|
||||
.c.obj:
|
||||
$(CC) $(CFLAGS) -c $< -Fo$@
|
||||
|
||||
.cpp.obj:
|
||||
$(CC) $(CFLAGS) -c $< -Fo$@
|
||||
|
||||
$(DLL): mod_security2_config.h $(OBJS)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -LD $(OBJS) -Fe$(DLL) $(LIBS) /link
|
||||
IF EXIST $(DLL).manifest $(MT) -manifest $(DLL).manifest -outputresource:$(DLL);2
|
||||
|
||||
install: $(DLL)
|
||||
copy $(DLL) $(BASE)\modules
|
||||
|
||||
clean:
|
||||
del $(OBJS) $(DLL) *.dll *.lib *.pdb *.idb *.ilk *.exp *.res *.rc *.bin mod_security2_config.h *.manifest
|
||||
@@ -1,810 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
|
||||
/* 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 <apr_lib.h>
|
||||
#define utf8_lcase(a) apr_tolower(a)
|
||||
#endif
|
||||
|
||||
#include <apr_tables.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
*******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies sibling nodes and child node for from given "from" node to "to" node.
|
||||
* Both nodes must already exist.
|
||||
*/
|
||||
static void acmp_copy_nodes_recursive(acmp_node_t *from, acmp_node_t *to, apr_pool_t *pool) {
|
||||
acmp_node_t *old_node = from->child, *new_node, *nn2;
|
||||
if (old_node == NULL) return;
|
||||
nn2 = apr_pcalloc(pool, sizeof(acmp_node_t));
|
||||
/* ENH: Check alloc succeded */
|
||||
acmp_clone_node_no_state(old_node, nn2);
|
||||
nn2->parent = to;
|
||||
to->child = nn2;
|
||||
acmp_copy_nodes_recursive(from->child, to->child, pool);
|
||||
|
||||
for (;;) {
|
||||
old_node = old_node->sibling;
|
||||
if (old_node == NULL) break;
|
||||
new_node = apr_pcalloc(pool, sizeof(acmp_node_t));
|
||||
/* ENH: Check alloc succeded */
|
||||
acmp_clone_node_no_state(old_node, new_node);
|
||||
new_node->parent = to;
|
||||
nn2->sibling = new_node;
|
||||
nn2 = new_node;
|
||||
acmp_copy_nodes_recursive(old_node, new_node, pool);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears hit count of each node, called from acmp_reset()
|
||||
*/
|
||||
static void acmp_clear_hit_count_recursive(acmp_node_t *node) {
|
||||
for (; node != NULL; node = node->sibling) {
|
||||
node->hit_count = 0;
|
||||
if (node->child != NULL) acmp_clear_hit_count_recursive(node->child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a match is found
|
||||
*/
|
||||
static void acmp_found(ACMP *parser, acmp_node_t *node) {
|
||||
if (node->callback) {
|
||||
node->callback(parser, node->callback_data,
|
||||
parser->bp_buffer[(parser->char_pos - node->depth - 1) % parser->bp_buff_len],
|
||||
parser->char_pos - node->depth - 1);
|
||||
}
|
||||
node->hit_count++;
|
||||
parser->hit_count++;
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
*******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys previously created parser
|
||||
*/
|
||||
void acmp_destroy(ACMP *parser) {
|
||||
/*
|
||||
* All data is kept in parser's pool (including parser struct itself), so
|
||||
* destroying the pool will destroy everything
|
||||
*/
|
||||
apr_pool_destroy(parser->pool);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
apr_status_t rc;
|
||||
apr_pool_t *p;
|
||||
ACMP *new_parser;
|
||||
|
||||
if (pool == NULL) pool = parser->parent_pool;
|
||||
rc = apr_pool_create(&p, pool);
|
||||
if (rc != APR_SUCCESS) return NULL;
|
||||
|
||||
new_parser = apr_pcalloc(p, sizeof(ACMP));
|
||||
/* ENH: Check alloc succeded */
|
||||
new_parser->pool = p;
|
||||
new_parser->parent_pool = pool;
|
||||
#ifdef ACMP_USE_UTF8
|
||||
new_parser->is_utf8 = parser->is_utf8;
|
||||
#endif
|
||||
new_parser->is_case_sensitive = parser->is_case_sensitive;
|
||||
new_parser->root_node = apr_pcalloc(p, sizeof(acmp_node_t));
|
||||
/* ENH: Check alloc succeded */
|
||||
new_parser->dict_count = parser->dict_count;
|
||||
new_parser->longest_entry = parser->longest_entry;
|
||||
acmp_copy_nodes_recursive(parser->root_node, new_parser->root_node, new_parser->pool);
|
||||
acmp_prepare(new_parser);
|
||||
return new_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to process incoming data stream
|
||||
* 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) {
|
||||
acmp_node_t *node, *go_to;
|
||||
#ifdef ACMP_USE_UTF8
|
||||
apr_size_t seq_length;
|
||||
#endif
|
||||
const char *end;
|
||||
|
||||
if (parser->is_failtree_done == 0) acmp_prepare(parser);
|
||||
|
||||
node = parser->active_node;
|
||||
end = data + len;
|
||||
|
||||
while (data < end) {
|
||||
acmp_utf8_char_t letter;
|
||||
|
||||
parser->bp_buffer[parser->char_pos % parser->bp_buff_len] = parser->byte_pos;
|
||||
#ifdef ACMP_USE_UTF8
|
||||
if (parser->is_utf8) {
|
||||
if (parser->u8buff_len > 0) {
|
||||
/* Resuming partial utf-8 sequence */
|
||||
seq_length = utf8_seq_len(parser->u8_buff);
|
||||
for (;;) {
|
||||
parser->u8_buff[parser->u8buff_len++] = *data++;
|
||||
if (parser->u8buff_len == seq_length) {
|
||||
parser->u8buff_len = 0;
|
||||
letter = utf8_decodechar(parser->u8_buff);
|
||||
parser->byte_pos += seq_length;
|
||||
parser->char_pos++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* not resuming partial sequence, reading from the stream */
|
||||
seq_length = utf8_seq_len(data);
|
||||
if ((data + seq_length) > end) {
|
||||
while (data < end) parser->u8_buff[parser->u8buff_len++] = *data++;
|
||||
return APR_SUCCESS;
|
||||
} else {
|
||||
letter = utf8_decodechar(data);
|
||||
data += seq_length;
|
||||
parser->byte_pos += seq_length;
|
||||
parser->char_pos++;
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
letter = *data++;
|
||||
parser->byte_pos++;
|
||||
parser->char_pos++;
|
||||
}
|
||||
if (parser->is_case_sensitive == 0) letter = utf8_lcase(letter);
|
||||
|
||||
go_to = NULL;
|
||||
while (go_to == NULL) {
|
||||
acmp_node_t *n2 = acmp_goto(node, letter);
|
||||
go_to = acmp_child_for_code(node, letter);
|
||||
if (n2 != go_to) {
|
||||
n2 = acmp_goto(node, letter);
|
||||
};
|
||||
if (go_to != NULL) {
|
||||
if (go_to->is_last) {
|
||||
acmp_found(parser, go_to);
|
||||
}
|
||||
}
|
||||
if (node == parser->root_node) break;
|
||||
if (go_to == NULL) node = node->fail;
|
||||
}
|
||||
if (go_to != NULL) node = go_to;
|
||||
|
||||
/* We need to collect other nodes that are last letters of phrase. These
|
||||
* will be fail node of current node if it has is_last flag set, and
|
||||
* fail node of that node, recursively down to root node.
|
||||
*/
|
||||
go_to = node;
|
||||
if (go_to != parser->root_node) {
|
||||
for (go_to = go_to->o_match; go_to != NULL; go_to = go_to->o_match) {
|
||||
acmp_found(parser, go_to);
|
||||
}
|
||||
}
|
||||
}
|
||||
parser->active_node = node;
|
||||
return parser->hit_count > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the state of parser so you can start using it with new set of data.
|
||||
*
|
||||
* No need to clear buffer since it will be re-initialized at first run of
|
||||
* acmp_process
|
||||
*/
|
||||
void acmp_reset(ACMP *parser) {
|
||||
parser->is_active = 0;
|
||||
parser->byte_pos = 0;
|
||||
parser->char_pos = 0;
|
||||
parser->hit_count = 0;
|
||||
parser->u8buff_len = 0;
|
||||
acmp_clear_hit_count_recursive(parser->root_node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ACMPT struct that will use parser's tree, without duplicating its data
|
||||
*/
|
||||
ACMPT *acmp_duplicate_quick(ACMP *parser, apr_pool_t *pool) {
|
||||
apr_pool_t *p = (pool != NULL) ? pool : parser->pool;
|
||||
ACMPT *dup = apr_pcalloc(p, sizeof(ACMPT));
|
||||
/* ENH: Check alloc succeded */
|
||||
dup->parser = parser;
|
||||
return dup;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#ifndef ACMP_H_
|
||||
#define ACMP_H_
|
||||
|
||||
#include <apr.h>
|
||||
#include <apr_pools.h>
|
||||
|
||||
#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_*/
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#ifndef _APACHE2_H_
|
||||
#define _APACHE2_H_
|
||||
|
||||
#include "http_core.h"
|
||||
#include "http_request.h"
|
||||
#include "httpd.h"
|
||||
#include "ap_release.h"
|
||||
|
||||
#include <apr_general.h>
|
||||
#include <apr_optional.h>
|
||||
|
||||
|
||||
#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));
|
||||
#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 internal_log_ex(request_rec *r, directory_config *dcfg, modsec_rec *msr,
|
||||
int level, int fixup, const char *text, va_list ap);
|
||||
|
||||
void DSOLOCAL internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr,
|
||||
int level, const char *text, va_list ap);
|
||||
|
||||
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 *em);
|
||||
|
||||
const DSOLOCAL char *get_response_protocol(request_rec *r);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,823 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#include <util_filter.h>
|
||||
|
||||
#include "modsecurity.h"
|
||||
#include "apache2.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) {
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
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_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) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Request body is larger than the "
|
||||
"configured limit (%ld).", msr->txcfg->reqbody_limit);
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (buflen != 0) {
|
||||
int rcbs = modsecurity_request_body_store(msr, buf, buflen, error_msg);
|
||||
if (rcbs < 0) {
|
||||
if (rcbs == -5) {
|
||||
*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;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
msr->reqbody_length += buflen;
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
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 (flatten_response_body(msr) < 0) {
|
||||
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 (flatten_response_body(msr) < 0) {
|
||||
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)) {
|
||||
record_time_checkpoint(msr, 3);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,433 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the current time and store for later.
|
||||
*/
|
||||
void record_time_checkpoint(modsec_rec *msr, int checkpoint_no) {
|
||||
char note[100], note_name[100];
|
||||
apr_time_t now;
|
||||
|
||||
now = apr_time_now();
|
||||
switch(checkpoint_no) {
|
||||
case 1 :
|
||||
msr->time_checkpoint_1 = now;
|
||||
break;
|
||||
case 2 :
|
||||
msr->time_checkpoint_2 = now;
|
||||
break;
|
||||
case 3 :
|
||||
msr->time_checkpoint_3 = now;
|
||||
break;
|
||||
default :
|
||||
msr_log(msr, 1, "Internal Error: Unknown checkpoint: %d", checkpoint_no);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Apache-specific stuff. */
|
||||
apr_snprintf(note, 99, "%" APR_TIME_T_FMT, (now - msr->request_time));
|
||||
apr_snprintf(note_name, 99, "mod_security-time%d", checkpoint_no);
|
||||
apr_table_set(msr->r->notes, note_name, note);
|
||||
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "Time #%d: %s", checkpoint_no, note);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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 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 = "";
|
||||
|
||||
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
|
||||
"[client %s] ModSecurity: %s%s [uri \"%s\"]%s", r->connection->remote_ip, str1,
|
||||
hostname, log_escape(msr->mp, r->uri), unique_id);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal log helper function. Use msr_log instead.
|
||||
*/
|
||||
void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr,
|
||||
int level, const char *text, va_list ap)
|
||||
{
|
||||
internal_log_ex(r, dcfg, msr, level, 0, text, ap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 *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;
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
Custom ModSecurity Modules
|
||||
--------------------------
|
||||
|
||||
This directory contains three examples how you can extend
|
||||
ModSecurity without having to touch it directly, simply
|
||||
by creating custom Apache modules.
|
||||
|
||||
NOTE: ModSecurity must be compiled with API support
|
||||
to use this feature (do not use -DNO_MODSEC_API).
|
||||
|
||||
|
||||
Building the Example Custom Modules
|
||||
-----------------------------------
|
||||
|
||||
1) Example custom transformation function module
|
||||
|
||||
Module mod_tfn_reverse.c creates a custom transformation
|
||||
function "reverse" that reverses the content it receives
|
||||
on input.
|
||||
|
||||
# Compile as a normal user
|
||||
apxs -ca mod_tfn_reverse.c
|
||||
|
||||
# Install as superuser
|
||||
sudo apxs -i mod_tfn_reverse.la
|
||||
|
||||
|
||||
2) Example custom operator module
|
||||
|
||||
Module mod_op_strstr.c creates a custom operator "strstr"
|
||||
that implements fast matching using the Boyer-Moore-Horspool
|
||||
algorithm.
|
||||
|
||||
Compiling this module is more involved because it requires
|
||||
access to ModSecurity structures.
|
||||
|
||||
# Compile as a normal user
|
||||
apxs -I<MODSECURITY_SOURCE_CODE> -I/usr/include/libxml2 \
|
||||
-ca mod_op_strstr.c
|
||||
|
||||
# Install as superuser
|
||||
sudo apxs -i mod_op_strstr.la
|
||||
|
||||
|
||||
3) Example custom target variable module
|
||||
|
||||
Module mod_var_remote_addr_port.c creates a custom variable "REMOTE_ADDR_PORT"
|
||||
that combines the REMOTE_ADDR and REMOTE_PORT into a.b.c.d:port format.
|
||||
|
||||
Compiling this module is more involved because it requires
|
||||
access to ModSecurity structures.
|
||||
|
||||
# Compile as a normal user
|
||||
apxs -I<MODSECURITY_SOURCE_CODE> -I/usr/include/libxml2 \
|
||||
-ca mod_var_remote_addr_port.c
|
||||
|
||||
# Install as superuser
|
||||
sudo apxs -i mod_var_remote_addr_port.la
|
||||
|
||||
|
||||
Using the Modules
|
||||
-----------------
|
||||
|
||||
Once the modules are built and installed, you load them like any other Apache module, but they must be loaded *after* the mod_security2.so module.
|
||||
|
||||
# Load ModSecurity
|
||||
LoadModule security2_module modules/mod_security2.so
|
||||
|
||||
# Load ModSecurity custom modules
|
||||
LoadModule tfn_reverse_module modules/mod_tfn_reverse.so
|
||||
LoadModule op_strstr_module modules/mod_op_strstr.so
|
||||
LoadModule var_remote_addr_port_module modules/mod_var_remote_addr_port.so
|
||||
|
||||
# All three custom var/op/tfn used
|
||||
SecRule REMOTE_ADDR_PORT "@strstr 1.2.3.4:5678" "t:reverse"
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
|
||||
#include "httpd.h"
|
||||
#include "http_core.h"
|
||||
#include "http_config.h"
|
||||
#include "http_log.h"
|
||||
#include "http_protocol.h"
|
||||
#include "ap_config.h"
|
||||
#include "apr_optional.h"
|
||||
|
||||
#include "modsecurity.h"
|
||||
|
||||
#define ALPHABET_SIZE 256
|
||||
#define MAX_PATTERN_SIZE 64
|
||||
|
||||
static void initBoyerMooreHorspool(const char *pattern, int patlength,
|
||||
int *bm_badcharacter_array);
|
||||
|
||||
static int BoyerMooreHorspool(const char *pattern, int patlength,
|
||||
const char *text, int textlen, int *bm_badcharacter_array);
|
||||
|
||||
/**
|
||||
* Operator parameter initialisation entry point.
|
||||
*/
|
||||
static int op_strstr_init(msre_rule *rule, char **error_msg) {
|
||||
/* Operator initialisation function will be called once per
|
||||
* statement where operator is used. It is meant to be used
|
||||
* to check the parameters to see whether they are present
|
||||
* and if they are in the correct format.
|
||||
*/
|
||||
|
||||
/* In this example we just look for a simple non-empty parameter. */
|
||||
if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) {
|
||||
*error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'strstr'.");
|
||||
return 0; /* ERROR */
|
||||
}
|
||||
|
||||
/* If you need to transform the data in the parameter into something
|
||||
* else you should do that here. Simply create a new structure to hold
|
||||
* the transformed data and place the pointer to it into rule->op_param_data.
|
||||
* You will have access to this pointer later on.
|
||||
*/
|
||||
rule->op_param_data = apr_pcalloc(rule->ruleset->mp, ALPHABET_SIZE * sizeof(int));
|
||||
initBoyerMooreHorspool(rule->op_param, strlen(rule->op_param), (int *)rule->op_param_data);
|
||||
|
||||
/* OK */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Operator execution entry point.
|
||||
*/
|
||||
static int op_strstr_exec(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
|
||||
/* Here we need to inspect the contents of the supplied variable. */
|
||||
|
||||
/* In a general case it is possible for the value
|
||||
* to be NULL. What you need to do in this case
|
||||
* depends on your operator. In this example we return
|
||||
* a "no match" response.
|
||||
*/
|
||||
if (var->value == NULL) return 0; /* No match. */
|
||||
|
||||
/* Another thing to note is that variables are not C strings,
|
||||
* meaning the NULL byte is not used to determine the end
|
||||
* of the string. Variable length var->value_len should be
|
||||
* used for this purpose.
|
||||
*/
|
||||
|
||||
if (BoyerMooreHorspool(rule->op_param, strlen(rule->op_param),
|
||||
var->value, var->value_len, (int *)rule->op_param_data) >= 0)
|
||||
{
|
||||
return 1; /* Match. */
|
||||
}
|
||||
|
||||
return 0; /* No match. */
|
||||
}
|
||||
|
||||
static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp) {
|
||||
void (*fn)(const char *name, void *fn_init, void *fn_exec);
|
||||
|
||||
/* Look for the registration function
|
||||
* exported by ModSecurity.
|
||||
*/
|
||||
fn = APR_RETRIEVE_OPTIONAL_FN(modsec_register_operator);
|
||||
if (fn) {
|
||||
/* Use it to register our new
|
||||
* transformation function under the
|
||||
* name "reverse".
|
||||
*/
|
||||
fn("strstr", (void *)op_strstr_init, (void *)op_strstr_exec);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void register_hooks(apr_pool_t *p) {
|
||||
ap_hook_pre_config(hook_pre_config, NULL, NULL, APR_HOOK_LAST);
|
||||
}
|
||||
|
||||
/* Dispatch list for API hooks */
|
||||
module AP_MODULE_DECLARE_DATA op_strstr_module = {
|
||||
STANDARD20_MODULE_STUFF,
|
||||
NULL, /* create per-dir config structures */
|
||||
NULL, /* merge per-dir config structures */
|
||||
NULL, /* create per-server config structures */
|
||||
NULL, /* merge per-server config structures */
|
||||
NULL, /* table of config file commands */
|
||||
register_hooks /* register hooks */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
This example uses an implementation Boyer-Moore-Horspool
|
||||
matching algorithm as implemented in Streamline (http://ffpf.sourceforge.net).
|
||||
|
||||
Copyright (c) 2004-2006, Vrije Universiteit Amsterdam
|
||||
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 the Vrije Universiteit nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
*/
|
||||
|
||||
static void precompute_badcharacter(const char *pattern, int patlength,
|
||||
int bm_badcharacter_array[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ALPHABET_SIZE; ++i) {
|
||||
bm_badcharacter_array[i] = patlength;
|
||||
}
|
||||
|
||||
for (i = 0; i < patlength - 1; ++i){
|
||||
bm_badcharacter_array[(uint8_t)pattern[i]] = patlength - i - 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void initBoyerMooreHorspool(const char *pattern, int patlength,
|
||||
int *bm_badcharacter_array)
|
||||
{
|
||||
precompute_badcharacter(pattern,
|
||||
(patlength < MAX_PATTERN_SIZE ? patlength : MAX_PATTERN_SIZE), bm_badcharacter_array);
|
||||
}
|
||||
|
||||
static int BoyerMooreHorspool(const char *pattern, int patlength,
|
||||
const char *text, int textlen, int *bm_badcharacter_array)
|
||||
{
|
||||
int j;
|
||||
char c;
|
||||
|
||||
j = 0;
|
||||
while (j <= textlen - patlength) {
|
||||
c = text[j + patlength - 1];
|
||||
if (pattern[patlength - 1] == c && memcmp(pattern, text + j, patlength - 1) == 0) {
|
||||
return j;
|
||||
}
|
||||
j += bm_badcharacter_array[(uint8_t)c];
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
|
||||
#include "httpd.h"
|
||||
#include "http_core.h"
|
||||
#include "http_config.h"
|
||||
#include "http_log.h"
|
||||
#include "http_protocol.h"
|
||||
#include "ap_config.h"
|
||||
#include "apr_optional.h"
|
||||
|
||||
/* Must be declared if modsecurity.h is not included */
|
||||
APR_DECLARE_OPTIONAL_FN(void, modsec_register_tfn, (const char *name, void *fn));
|
||||
|
||||
|
||||
/**
|
||||
* This function will be invoked by
|
||||
* ModSecurity to transform input.
|
||||
*/
|
||||
static int reverse(apr_pool_t *mptmp, unsigned char *input,
|
||||
long int input_len, char **rval, long int *rval_len)
|
||||
{
|
||||
/* Transformation functions can choose to do their
|
||||
* thing in-place, overwriting the existing content. This
|
||||
* is normally possible only if the transformed content
|
||||
* is of equal length or shorter.
|
||||
*
|
||||
* If you need to expand the content use the temporary
|
||||
* memory pool mptmp to allocate the space.
|
||||
*/
|
||||
|
||||
/* Reverse the string in place, but only if it's long enough. */
|
||||
if (input_len > 1) {
|
||||
long int i = 0;
|
||||
long int j = input_len - 1;
|
||||
while(i < j) {
|
||||
char c = input[i];
|
||||
input[i] = input[j];
|
||||
input[j] = c;
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tell ModSecurity about the content
|
||||
* we have generated. In this case we
|
||||
* merely point back to the input buffer.
|
||||
*/
|
||||
*rval = (char *)input;
|
||||
*rval_len = input_len;
|
||||
|
||||
/* Must return 1 if the content was
|
||||
* changed, or 0 otherwise.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp) {
|
||||
void (*fn)(const char *name, void *fn);
|
||||
|
||||
/* Look for the registration function
|
||||
* exported by ModSecurity.
|
||||
*/
|
||||
fn = APR_RETRIEVE_OPTIONAL_FN(modsec_register_tfn);
|
||||
if (fn) {
|
||||
/* Use it to register our new
|
||||
* transformation function under the
|
||||
* name "reverse".
|
||||
*/
|
||||
fn("reverse", (void *)reverse);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void register_hooks(apr_pool_t *p) {
|
||||
ap_hook_pre_config(hook_pre_config, NULL, NULL, APR_HOOK_LAST);
|
||||
}
|
||||
|
||||
/* Dispatch list for API hooks */
|
||||
module AP_MODULE_DECLARE_DATA tfn_reverse_module = {
|
||||
STANDARD20_MODULE_STUFF,
|
||||
NULL, /* create per-dir config structures */
|
||||
NULL, /* merge per-dir config structures */
|
||||
NULL, /* create per-server config structures */
|
||||
NULL, /* merge per-server config structures */
|
||||
NULL, /* table of config file commands */
|
||||
register_hooks /* register hooks */
|
||||
};
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
|
||||
#include "httpd.h"
|
||||
#include "http_core.h"
|
||||
#include "http_config.h"
|
||||
#include "http_log.h"
|
||||
#include "http_protocol.h"
|
||||
#include "ap_config.h"
|
||||
#include "apr_optional.h"
|
||||
|
||||
#include "re.h"
|
||||
|
||||
/* -- Generic generators/validators from re_variables.c -- */
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
|
||||
/* -- Module specific code -- */
|
||||
|
||||
/**
|
||||
* Create a silly variable with value = a.b.c.d:port
|
||||
*/
|
||||
static int var_remote_addr_port_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
const char *value = apr_psprintf(mptmp, "%s:%d", msr->remote_addr, msr->remote_port);
|
||||
|
||||
return var_simple_generate(var, vartab, mptmp, value);
|
||||
}
|
||||
|
||||
static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp) {
|
||||
void (*register_fn)(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);
|
||||
|
||||
/* Look for the registration function
|
||||
* exported by ModSecurity.
|
||||
*/
|
||||
register_fn = APR_RETRIEVE_OPTIONAL_FN(modsec_register_variable);
|
||||
if (register_fn) {
|
||||
/* Use it to register our new
|
||||
* variable under the
|
||||
* name "REMOTE_ADDR_PORT".
|
||||
*/
|
||||
register_fn(
|
||||
"REMOTE_ADDR_PORT",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_remote_addr_port_generate,
|
||||
VAR_DONT_CACHE,
|
||||
PHASE_REQUEST_HEADERS
|
||||
);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void register_hooks(apr_pool_t *p) {
|
||||
ap_hook_pre_config(hook_pre_config, NULL, NULL, APR_HOOK_LAST);
|
||||
}
|
||||
|
||||
/* Dispatch list for API hooks */
|
||||
module AP_MODULE_DECLARE_DATA var_remote_addr_port_module = {
|
||||
STANDARD20_MODULE_STUFF,
|
||||
NULL, /* create per-dir config structures */
|
||||
NULL, /* merge per-dir config structures */
|
||||
NULL, /* create per-server config structures */
|
||||
NULL, /* merge per-server config structures */
|
||||
NULL, /* table of config file commands */
|
||||
register_hooks /* register hooks */
|
||||
};
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -rf autom4te.cache
|
||||
|
||||
#automake --add-missing --copy
|
||||
autoreconf --install
|
||||
@@ -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
|
||||
@@ -1,82 +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_LDFLAGS=""
|
||||
APR_LIBS=""
|
||||
APR_LINK_LD=""
|
||||
|
||||
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_CFLAGS="`${APR_CONFIG} --includes --cppflags --cflags`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr CFLAGS: $APR_CFLAGS); fi
|
||||
APR_LDFLAGS="`${APR_CONFIG} --ldflags`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr LDFLAGS: $APR_LDFLAGS); fi
|
||||
APR_LIBS="`${APR_CONFIG} --libs`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr LIBS: $APR_LIBS); fi
|
||||
APR_LINK_LD="`${APR_CONFIG} --link-ld`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr LINK_LD: $APR_LINK_LD); fi
|
||||
CFLAGS=$save_CFLAGS
|
||||
LDFLAGS=$save_LDFLAGS
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
|
||||
AC_SUBST(APR_LIBS)
|
||||
AC_SUBST(APR_CFLAGS)
|
||||
AC_SUBST(APR_LDFLAGS)
|
||||
AC_SUBST(APR_LINK_LD)
|
||||
|
||||
if test -z "${APR_LIBS}"; then
|
||||
AC_MSG_NOTICE([*** apr library not found.])
|
||||
ifelse([$2], , AC_MSG_ERROR([apr library is required]), $2)
|
||||
else
|
||||
AC_MSG_NOTICE([using '${APR_LIBS}' for apr Library])
|
||||
ifelse([$1], , , $1)
|
||||
fi
|
||||
])
|
||||
@@ -1,82 +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_LIBS=""
|
||||
APU_LINK_LD=""
|
||||
|
||||
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 /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_CFLAGS="`${APU_CONFIG} --includes`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu CFLAGS: $APU_CFLAGS); fi
|
||||
APU_LDFLAGS="`${APU_CONFIG} --ldflags`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LDFLAGS: $APU_LDFLAGS); fi
|
||||
APU_LIBS="`${APU_CONFIG} --libs`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LIBS: $APU_LIBS); fi
|
||||
APU_LINK_LD="`${APU_CONFIG} --link-ld`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LINK_LD: $APU_LINK_LD); fi
|
||||
CFLAGS=$save_CFLAGS
|
||||
LDFLAGS=$save_LDFLAGS
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
|
||||
AC_SUBST(APU_LIBS)
|
||||
AC_SUBST(APU_CFLAGS)
|
||||
AC_SUBST(APU_LDFLAGS)
|
||||
AC_SUBST(APU_LINK_LD)
|
||||
|
||||
if test -z "${APU_LIBS}"; then
|
||||
AC_MSG_NOTICE([*** apu library not found.])
|
||||
ifelse([$2], , AC_MSG_ERROR([apu library is required]), $2)
|
||||
else
|
||||
AC_MSG_NOTICE([using '${APU_LIBS}' for apu Library])
|
||||
ifelse([$1], , , $1)
|
||||
fi
|
||||
])
|
||||
@@ -1,100 +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_CFLAGS=""
|
||||
CURL_LIBS=""
|
||||
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_CFLAGS="`${CURL_CONFIG} --cflags`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(curl CFLAGS: $CURL_CFLAGS); fi
|
||||
CURL_LIBS="`${CURL_CONFIG} --libs`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(curl LIBS: $CURL_LIBS); fi
|
||||
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
|
||||
CFLAGS=$save_CFLAGS
|
||||
LDFLAGS=$save_LDFLAGS
|
||||
|
||||
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])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_NOTICE([NOTE: curl library may be too old: $CURL_VERSION])
|
||||
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_LIBS)
|
||||
AC_SUBST(CURL_CFLAGS)
|
||||
AC_SUBST(CURL_USES_GNUTLS)
|
||||
|
||||
if test -z "${CURL_LIBS}"; 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_LIBS}' for curl Library])
|
||||
ifelse([$1], , , $1)
|
||||
fi
|
||||
])
|
||||
@@ -1,184 +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
|
||||
|
||||
LUA_CONFIG=""
|
||||
LUA_CFLAGS=""
|
||||
LUA_LIBS=""
|
||||
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_DEFUN([CHECK_LUA],
|
||||
[dnl
|
||||
|
||||
AC_ARG_WITH(
|
||||
lua,
|
||||
[AC_HELP_STRING([--with-lua=PATH],[Path to lua prefix or config script])],
|
||||
[test_paths="${with_lua}"],
|
||||
[test_paths="/usr/local/liblua /usr/local/lua /usr/local /opt/liblua /opt/lua /opt /usr"; ])
|
||||
|
||||
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_CFLAGS="`${LUA_CONFIG} ${LUA_PKGNAME} --cflags`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(lua CFLAGS: $LUA_CFLAGS); fi
|
||||
LUA_LIBS="`${LUA_CONFIG} ${LUA_PKGNAME} --libs`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(lua LIBS: $LUA_LIBS); fi
|
||||
CFLAGS=$save_CFLAGS
|
||||
LDFLAGS=$save_LDFLAGS
|
||||
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_CFLAGS="-I${lua_inc_path}"
|
||||
LUA_LIBS="-L${lua_lib_path} -l${lua_lib_name}"
|
||||
CFLAGS=$save_CFLAGS
|
||||
LDFLAGS=$save_LDFLAGS
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "${LUA_LIBS}"; then
|
||||
LUA_CFLAGS="-DWITH_LUA ${LUA_CFLAGS}"
|
||||
fi
|
||||
|
||||
AC_SUBST(LUA_LIBS)
|
||||
AC_SUBST(LUA_CFLAGS)
|
||||
|
||||
if test "${with_path}" != "no"; then
|
||||
if test -z "${LUA_LIBS}"; then
|
||||
ifelse([$2], , AC_MSG_NOTICE([optional lua library not found]), $2)
|
||||
else
|
||||
AC_MSG_NOTICE([using '${LUA_LIBS}' for lua Library])
|
||||
ifelse([$1], , , $1)
|
||||
fi
|
||||
fi
|
||||
])
|
||||
@@ -1,81 +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_CFLAGS=""
|
||||
PCRE_LIBS=""
|
||||
|
||||
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
|
||||
CFLAGS=$save_CFLAGS
|
||||
LDFLAGS=$save_LDFLAGS
|
||||
|
||||
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_CFLAGS="`${PCRE_CONFIG} --cflags`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre CFLAGS: $PCRE_CFLAGS); fi
|
||||
PCRE_LIBS="`${PCRE_CONFIG} --libs`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre LIBS: $PCRE_LIBS); fi
|
||||
CFLAGS=$save_CFLAGS
|
||||
LDFLAGS=$save_LDFLAGS
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
|
||||
AC_SUBST(PCRE_LIBS)
|
||||
AC_SUBST(PCRE_CFLAGS)
|
||||
|
||||
if test -z "${PCRE_LIBS}"; then
|
||||
AC_MSG_NOTICE([*** pcre library not found.])
|
||||
ifelse([$2], , AC_MSG_ERROR([pcre library is required]), $2)
|
||||
else
|
||||
AC_MSG_NOTICE([using '${PCRE_LIBS}' for pcre Library])
|
||||
ifelse([$1], , , $1)
|
||||
fi
|
||||
])
|
||||
@@ -1,74 +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_CFLAGS=""
|
||||
LIBXML2_LIBS=""
|
||||
|
||||
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
|
||||
CFLAGS=$save_CFLAGS
|
||||
LDFLAGS=$save_LDFLAGS
|
||||
|
||||
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_CFLAGS="`${LIBXML2_CONFIG} --cflags`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(xml CFLAGS: $LIBXML2_CFLAGS); fi
|
||||
LIBXML2_LIBS="`${LIBXML2_CONFIG} --libs`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(xml LIBS: $LIBXML2_LIBS); fi
|
||||
CFLAGS=$save_CFLAGS
|
||||
LDFLAGS=$save_LDFLAGS
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
|
||||
AC_SUBST(LIBXML2_LIBS)
|
||||
AC_SUBST(LIBXML2_CFLAGS)
|
||||
|
||||
if test -z "${LIBXML2_LIBS}"; then
|
||||
AC_MSG_NOTICE([*** xml library not found.])
|
||||
ifelse([$2], , AC_MSG_ERROR([libxml2 is required]), $2)
|
||||
else
|
||||
AC_MSG_NOTICE([using '${LIBXML2_LIBS}' for libxml2])
|
||||
ifelse([$1], , , $1)
|
||||
fi
|
||||
])
|
||||
@@ -1,224 +0,0 @@
|
||||
#!/bin/sh
|
||||
##
|
||||
## install.sh -- install a program, script or datafile
|
||||
##
|
||||
## Based on `install-sh' from the X Consortium's X11R5 distribution
|
||||
## as of 89/12/18 which is freely available.
|
||||
## Cleaned up for Apache's Autoconf-style Interface (APACI)
|
||||
## by Ralf S. Engelschall <rse@apache.org>
|
||||
##
|
||||
#
|
||||
# This script falls under the Apache License.
|
||||
# See http://www.apache.org/docs/LICENSE
|
||||
|
||||
|
||||
#
|
||||
# put in absolute paths if you don't have them in your path;
|
||||
# or use env. vars.
|
||||
#
|
||||
mvprog="${MVPROG-mv}"
|
||||
cpprog="${CPPROG-cp}"
|
||||
chmodprog="${CHMODPROG-chmod}"
|
||||
chownprog="${CHOWNPROG-chown}"
|
||||
chgrpprog="${CHGRPPROG-chgrp}"
|
||||
stripprog="${STRIPPROG-strip}"
|
||||
rmprog="${RMPROG-rm}"
|
||||
|
||||
#
|
||||
# parse argument line
|
||||
#
|
||||
instcmd="$mvprog"
|
||||
chmodcmd=""
|
||||
chowncmd=""
|
||||
chgrpcmd=""
|
||||
stripcmd=""
|
||||
rmcmd="$rmprog -f"
|
||||
mvcmd="$mvprog"
|
||||
ext=""
|
||||
src=""
|
||||
dst=""
|
||||
while [ "x$1" != "x" ]; do
|
||||
case $1 in
|
||||
-c) instcmd="$cpprog"
|
||||
shift; continue
|
||||
;;
|
||||
-m) chmodcmd="$chmodprog $2"
|
||||
shift; shift; continue
|
||||
;;
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift; shift; continue
|
||||
;;
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift; shift; continue
|
||||
;;
|
||||
-s) stripcmd="$stripprog"
|
||||
shift; continue
|
||||
;;
|
||||
-S) stripcmd="$stripprog $2"
|
||||
shift; shift; continue
|
||||
;;
|
||||
-e) ext="$2"
|
||||
shift; shift; continue
|
||||
;;
|
||||
*) if [ "x$src" = "x" ]; then
|
||||
src=$1
|
||||
else
|
||||
dst=$1
|
||||
fi
|
||||
shift; continue
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [ "x$src" = "x" ]; then
|
||||
echo "install.sh: no input file specified"
|
||||
exit 1
|
||||
fi
|
||||
if [ "x$dst" = "x" ]; then
|
||||
echo "install.sh: no destination specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#
|
||||
# If destination is a directory, append the input filename; if
|
||||
# your system does not like double slashes in filenames, you may
|
||||
# need to add some logic
|
||||
#
|
||||
if [ -d $dst ]; then
|
||||
dst="$dst/`basename $src`"
|
||||
fi
|
||||
|
||||
# Add a possible extension (such as ".exe") to src and dst
|
||||
src="$src$ext"
|
||||
dst="$dst$ext"
|
||||
|
||||
# Make a temp file name in the proper directory.
|
||||
dstdir=`dirname $dst`
|
||||
dsttmp=$dstdir/#inst.$$#
|
||||
|
||||
# Move or copy the file name to the temp name
|
||||
$instcmd $src $dsttmp
|
||||
|
||||
# And set any options; do chmod last to preserve setuid bits
|
||||
if [ "x$chowncmd" != "x" ]; then $chowncmd $dsttmp; fi
|
||||
if [ "x$chgrpcmd" != "x" ]; then $chgrpcmd $dsttmp; fi
|
||||
if [ "x$stripcmd" != "x" ]; then $stripcmd $dsttmp; fi
|
||||
if [ "x$chmodcmd" != "x" ]; then $chmodcmd $dsttmp; fi
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$rmcmd $dst
|
||||
$mvcmd $dsttmp $dst
|
||||
|
||||
exit 0
|
||||
|
||||
#!/bin/sh
|
||||
##
|
||||
## install.sh -- install a program, script or datafile
|
||||
##
|
||||
## Based on `install-sh' from the X Consortium's X11R5 distribution
|
||||
## as of 89/12/18 which is freely available.
|
||||
## Cleaned up for Apache's Autoconf-style Interface (APACI)
|
||||
## by Ralf S. Engelschall <rse@apache.org>
|
||||
##
|
||||
#
|
||||
# This script falls under the Apache License.
|
||||
# See http://www.apache.org/docs/LICENSE
|
||||
|
||||
|
||||
#
|
||||
# put in absolute paths if you don't have them in your path;
|
||||
# or use env. vars.
|
||||
#
|
||||
mvprog="${MVPROG-mv}"
|
||||
cpprog="${CPPROG-cp}"
|
||||
chmodprog="${CHMODPROG-chmod}"
|
||||
chownprog="${CHOWNPROG-chown}"
|
||||
chgrpprog="${CHGRPPROG-chgrp}"
|
||||
stripprog="${STRIPPROG-strip}"
|
||||
rmprog="${RMPROG-rm}"
|
||||
|
||||
#
|
||||
# parse argument line
|
||||
#
|
||||
instcmd="$mvprog"
|
||||
chmodcmd=""
|
||||
chowncmd=""
|
||||
chgrpcmd=""
|
||||
stripcmd=""
|
||||
rmcmd="$rmprog -f"
|
||||
mvcmd="$mvprog"
|
||||
ext=""
|
||||
src=""
|
||||
dst=""
|
||||
while [ "x$1" != "x" ]; do
|
||||
case $1 in
|
||||
-c) instcmd="$cpprog"
|
||||
shift; continue
|
||||
;;
|
||||
-m) chmodcmd="$chmodprog $2"
|
||||
shift; shift; continue
|
||||
;;
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift; shift; continue
|
||||
;;
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift; shift; continue
|
||||
;;
|
||||
-s) stripcmd="$stripprog"
|
||||
shift; continue
|
||||
;;
|
||||
-S) stripcmd="$stripprog $2"
|
||||
shift; shift; continue
|
||||
;;
|
||||
-e) ext="$2"
|
||||
shift; shift; continue
|
||||
;;
|
||||
*) if [ "x$src" = "x" ]; then
|
||||
src=$1
|
||||
else
|
||||
dst=$1
|
||||
fi
|
||||
shift; continue
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [ "x$src" = "x" ]; then
|
||||
echo "install.sh: no input file specified"
|
||||
exit 1
|
||||
fi
|
||||
if [ "x$dst" = "x" ]; then
|
||||
echo "install.sh: no destination specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#
|
||||
# If destination is a directory, append the input filename; if
|
||||
# your system does not like double slashes in filenames, you may
|
||||
# need to add some logic
|
||||
#
|
||||
if [ -d $dst ]; then
|
||||
dst="$dst/`basename $src`"
|
||||
fi
|
||||
|
||||
# Add a possible extension (such as ".exe") to src and dst
|
||||
src="$src$ext"
|
||||
dst="$dst$ext"
|
||||
|
||||
# Make a temp file name in the proper directory.
|
||||
dstdir=`dirname $dst`
|
||||
dsttmp=$dstdir/#inst.$$#
|
||||
|
||||
# Move or copy the file name to the temp name
|
||||
$instcmd $src $dsttmp
|
||||
|
||||
# And set any options; do chmod last to preserve setuid bits
|
||||
if [ "x$chowncmd" != "x" ]; then $chowncmd $dsttmp; fi
|
||||
if [ "x$chgrpcmd" != "x" ]; then $chgrpcmd $dsttmp; fi
|
||||
if [ "x$stripcmd" != "x" ]; then $stripcmd $dsttmp; fi
|
||||
if [ "x$chmodcmd" != "x" ]; then $chmodcmd $dsttmp; fi
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$rmcmd $dst
|
||||
$mvcmd $dsttmp $dst
|
||||
|
||||
exit 0
|
||||
|
||||
7373
2.5.13/2.5.x/apache2/build/libtool.m4
vendored
7373
2.5.13/2.5.x/apache2/build/libtool.m4
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
6564
2.5.13/2.5.x/apache2/configure
vendored
6564
2.5.13/2.5.x/apache2/configure
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,441 +0,0 @@
|
||||
dnl
|
||||
dnl Autoconf configuration for ModSecurity
|
||||
dnl
|
||||
dnl Use ./buildconf to produce a configure script
|
||||
dnl
|
||||
|
||||
AC_PREREQ(2.63)
|
||||
|
||||
AC_INIT
|
||||
dnl AC_INIT(ModSecurity, 2.5, mod-security-users@lists.sourceforge.net, modsecurity-apache)
|
||||
AC_CONFIG_SRCDIR([mod_security2.c])
|
||||
AC_CONFIG_HEADER([mod_security2_config.h])
|
||||
AC_CONFIG_AUX_DIR([build])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_AWK
|
||||
AC_PROG_CC
|
||||
AC_PROG_CPP
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_LN_S
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_RANLIB
|
||||
AC_PROG_GREP
|
||||
AC_PATH_PROGS(PERL, [perl perl5], )
|
||||
AC_PATH_PROGS(ENV_CMD, [env printenv], )
|
||||
|
||||
# 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])
|
||||
|
||||
# Some directories
|
||||
MSC_BASE_DIR=`pwd`
|
||||
MSC_PKGBASE_DIR="$MSC_BASE_DIR/.."
|
||||
MSC_TEST_DIR="$MSC_BASE_DIR/t"
|
||||
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
|
||||
|
||||
# Add PCRE Studying
|
||||
|
||||
AC_ARG_ENABLE(pcre-study,
|
||||
AS_HELP_STRING([--enable-pcre-study],
|
||||
[Enable PCRE regex studying during configure.]),
|
||||
[
|
||||
if test "$enableval" != "no"; then
|
||||
pcre_study='-DWITH_PCRE_STUDY'
|
||||
else
|
||||
pcre_study=''
|
||||
fi
|
||||
],
|
||||
[
|
||||
pcre_study='-DWITH_PCRE_STUDY'
|
||||
])
|
||||
|
||||
# Limit PCRE matching
|
||||
AC_ARG_ENABLE(pcre-match-limit,
|
||||
AS_HELP_STRING([--enable-pcre-match-limit],
|
||||
[Enable PCRE regex match limit during configure.]),
|
||||
[
|
||||
if test "$enableval" = "yes"; then
|
||||
AC_MSG_ERROR([PCRE match limits require a numeric value])
|
||||
elif test "$enableval" = "no"; then
|
||||
pcre_match_limit=''
|
||||
else
|
||||
pcre_match_limit="-DMODSEC_PCRE_MATCH_LIMIT=$enableval"
|
||||
fi
|
||||
],
|
||||
[
|
||||
pcre_match_limit='-DMODSEC_PCRE_MATCH_LIMIT=1500'
|
||||
])
|
||||
|
||||
# Limit PCRE matching recursion
|
||||
AC_ARG_ENABLE(pcre-match-limit-recursion,
|
||||
AS_HELP_STRING([--enable-pcre-match-limit-recursion],
|
||||
[Enable PCRE regex match limit recursion during configure.]),
|
||||
[
|
||||
if test "$enableval" = "yes"; then
|
||||
AC_MSG_ERROR([PCRE match limits require a numeric value])
|
||||
elif test "$enableval" = "no"; then
|
||||
pcre_match_limit_recursion=''
|
||||
else
|
||||
pcre_match_limit_recursion="-DMODSEC_PCRE_MATCH_LIMIT_RECURSION=$enableval"
|
||||
fi
|
||||
],
|
||||
[
|
||||
pcre_match_limit_recursion='-DMODSEC_PCRE_MATCH_LIMIT_RECURSION=1500'
|
||||
])
|
||||
|
||||
# Ignore configure errors
|
||||
AC_ARG_ENABLE(errors,
|
||||
AS_HELP_STRING([--disable-errors],
|
||||
[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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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
|
||||
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
|
||||
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="`$APXS -q CFLAGS` `$APXS -q EXTRA_CFLAGS`"
|
||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs CFLAGS: $APXS_CFLAGS); fi
|
||||
APXS_LDFLAGS="`$APXS -q LDFLAGS` `$APXS -q EXTRA_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
|
||||
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
|
||||
else
|
||||
if test "$report_errors" -eq 1; then
|
||||
AC_MSG_ERROR(couldn't find APXS)
|
||||
else
|
||||
AC_MSG_NOTICE(couldn't find APXS)
|
||||
fi
|
||||
fi
|
||||
|
||||
# Include M4 macros
|
||||
sinclude(build/find_pcre.m4)
|
||||
sinclude(build/find_apr.m4)
|
||||
sinclude(build/find_apu.m4)
|
||||
sinclude(build/find_xml.m4)
|
||||
sinclude(build/find_lua.m4)
|
||||
sinclude(build/find_curl.m4)
|
||||
|
||||
|
||||
### Build *EXTRA_CFLAGS vars
|
||||
|
||||
# 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
|
||||
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 $debug_conf $debug_cache $debug_acmp $debug_mem $perf_meas $modsec_api"
|
||||
|
||||
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
|
||||
|
||||
save_CPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="$APXS_INCLUDES $CPPFLAGS"
|
||||
save_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="$APXS_LDFLAGS $LDFLAGS"
|
||||
|
||||
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_HTTPD)
|
||||
|
||||
CHECK_PCRE()
|
||||
CHECK_APR()
|
||||
CHECK_APU()
|
||||
CHECK_LIBXML2()
|
||||
CHECK_LUA()
|
||||
CHECK_CURL()
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_CONFIG_FILES([build/apxs-wrapper], [chmod +x build/apxs-wrapper])
|
||||
if test -e "$PERL"; then
|
||||
AC_CONFIG_FILES([mlogc-src/mlogc-batch-load.pl], [chmod +x mlogc-src/mlogc-batch-load.pl])
|
||||
AC_CONFIG_FILES([t/run-unit-tests.pl], [chmod +x t/run-unit-tests.pl])
|
||||
AC_CONFIG_FILES([t/run-regression-tests.pl], [chmod +x t/run-regression-tests.pl])
|
||||
AC_CONFIG_FILES([t/gen_rx-pm.pl], [chmod +x t/gen_rx-pm.pl])
|
||||
AC_CONFIG_FILES([t/csv_rx-pm.pl], [chmod +x t/csv_rx-pm.pl])
|
||||
AC_CONFIG_FILES([t/regression/server_root/conf/httpd.conf])
|
||||
|
||||
# Perl based tools
|
||||
AC_CONFIG_FILES([../tools/rules-updater.pl], [chmod +x ../tools/rules-updater.pl])
|
||||
fi
|
||||
if test -e "mlogc-src/Makefile.in"; then
|
||||
AC_CONFIG_FILES([mlogc-src/Makefile])
|
||||
fi
|
||||
|
||||
AC_OUTPUT
|
||||
@@ -1,76 +0,0 @@
|
||||
ModSecurity Audit Log Collector (mlogc)
|
||||
|
||||
Mlogc is used to connect a ModSecurity sensor to the central
|
||||
audit log repository.
|
||||
|
||||
To Install:
|
||||
===========
|
||||
|
||||
1) Copy the mlogc executable to an appropriate location.
|
||||
|
||||
A good location might be /usr/local/bin, /opt/mlogc/bin, etc.
|
||||
|
||||
2) Create sensor in the central audit log repository. Note the
|
||||
username and the password (SENSOR_USERNAME, SENSOR_PASSWORD).
|
||||
Also note the IP address central repository listens on
|
||||
(CONSOLE_IP_ADDRESS).
|
||||
|
||||
3) Configure the ModSecurity sensor to use mlogc
|
||||
|
||||
# Use ReleventOnly auditing
|
||||
SecAuditEngine RelevantOnly
|
||||
|
||||
# Must use concurrent logging
|
||||
SecAuditLogType Concurrent
|
||||
|
||||
# Send all audit log parts
|
||||
SecAuditLogParts ABIDEFGHZ
|
||||
|
||||
# Use the same /CollectorRoot/LogStorageDir as in mlogc.conf
|
||||
SecAuditLogStorageDir /var/log/mlogc/data
|
||||
|
||||
# Pipe audit log to mlogc with your configuration
|
||||
SecAuditLog "|/usr/local/bin/mlogc /etc/mlogc.conf"
|
||||
|
||||
4) Using the mlogc-default.conf as a template, configure the logger.
|
||||
|
||||
Typically these are the only directives that will need to be modified
|
||||
to conform to your site:
|
||||
|
||||
# Points to the root of the installation. All relative
|
||||
# paths configured in this file will be resolved with the
|
||||
# help of this path (LogStorageDir, TransactionLog, etc.)
|
||||
#
|
||||
# Typically, this will be the parent directory that is configured
|
||||
# in ModSecurity for the SecAuditLogStorageDirectory. So, if
|
||||
# your SecAuditLogStorageDirectory is set to /var/log/mlogc/data,
|
||||
# then set this to /var/log/mlogc.
|
||||
CollectorRoot "/var/log/mlogc"
|
||||
|
||||
# ModSecurity Console receiving URI. You can change the host
|
||||
# and the port parts but leave everything else as is.
|
||||
ConsoleURI https://CONSOLE_IP_ADDRESS:8886/rpc/auditLogReceiver
|
||||
|
||||
# Sensor credentials
|
||||
SensorUsername "SENSOR_USERNAME"
|
||||
SensorPassword "SENSOR_PASSWORD"
|
||||
|
||||
# Base directory where the audit logs are stored. This can be specified
|
||||
# as a path relative to the CollectorRoot, or a full path. It should
|
||||
# resolve to the same path as ModSecurity's SecAuditLogStorageDirectory.
|
||||
LogStorageDir "data"
|
||||
|
||||
See the mlogc-default.conf configuration file for details on other
|
||||
configuration directives.
|
||||
|
||||
5) Restart the ModSecurity sensor.
|
||||
|
||||
From now on every audit log generated will go to the repository. Make
|
||||
sure you create an alert. Transactions without alerts will be recorded
|
||||
but not displayed on the home page.
|
||||
|
||||
To troubleshoot, generate alerts and observe file "mlogc-error.log".
|
||||
|
||||
If mlogc fails to connect to the server it will pause for a period
|
||||
of time (60 seconds by default) before it will try again.
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
# Generated Makefile for ModSecurity Log Collector (mlogc)
|
||||
|
||||
CC = @CC@
|
||||
EXTRA_CFLAGS = @EXTRA_CFLAGS@
|
||||
|
||||
srcdir = .
|
||||
modsecsrcdir = $(srcdir)/..
|
||||
srclibdir = $(srcdir)/srclib
|
||||
|
||||
MLOGC_VERSION = `grep '^\#define *VERSION ' mlogc.c | sed 's/.*VERSION *"\([^"]*\)"/\1/'`
|
||||
|
||||
APR_FLAGS = @APR_CFLAGS@
|
||||
APR_LIBS = @APR_LINK_LD@ @APR_LIBS@
|
||||
|
||||
CURL_FLAGS = @CURL_CFLAGS@
|
||||
CURL_LIBS = @CURL_LIBS@
|
||||
|
||||
PCRE_FLAGS = @PCRE_CFLAGS@
|
||||
PCRE_LIBS = @PCRE_LIBS@
|
||||
|
||||
APR_S_FLAGS = `$(srclibdir)/install/apr/bin/apr-1-config --includes --cppflags --cflags`
|
||||
APR_S_LIBS = `$(srclibdir)/install/apr/bin/apr-1-config --link-ld`
|
||||
|
||||
CURL_S_FLAGS = `$(srclibdir)/install/curl/bin/curl-config --cflags`
|
||||
CURL_S_LIBS = `$(srclibdir)/install/curl/bin/curl-config --libs`
|
||||
|
||||
PCRE_S_FLAGS = `$(srclibdir)/install/pcre/bin/pcre-config --cflags`
|
||||
PCRE_S_LIBS = `$(srclibdir)/install/pcre/bin/pcre-config --libs`
|
||||
|
||||
all: mlogc
|
||||
|
||||
mlogc: mlogc.c
|
||||
@echo; \
|
||||
echo "Building dynamically linked mlogc..."; \
|
||||
$(CC) $(CFLAGS) -o mlogc mlogc.c \
|
||||
-I$(modsecsrcdir) \
|
||||
$(APR_FLAGS) $(CURL_FLAGS) $(PCRE_FLAGS) \
|
||||
$(APR_LIBS) $(CURL_LIBS) $(PCRE_LIBS); \
|
||||
chmod 755 mlogc; \
|
||||
echo; \
|
||||
echo "Build finished. Please follow the INSTALL instructions to complete the install."; \
|
||||
echo
|
||||
|
||||
.archives-ok:
|
||||
@if [ -n "$(MLOGC_NOVERIFY)" -a "$(MLOGC_NOVERIFY)" = "1" ]; then \
|
||||
touch .archives-ok; \
|
||||
else \
|
||||
$(srclibdir)/archives.sh && touch .archives-ok; \
|
||||
fi
|
||||
|
||||
.support-libs-ok:
|
||||
$(srclibdir)/build.sh && touch .support-libs-ok
|
||||
|
||||
archives: .archives-ok
|
||||
|
||||
support-libs: .support-libs-ok
|
||||
|
||||
clean-build:
|
||||
@rm -rf $(srclibdir)/build
|
||||
|
||||
clean-install:
|
||||
@rm -rf $(srclibdir)/install
|
||||
|
||||
clean-mlogc:
|
||||
@rm -rf core mlogc *~ *.o *.so *.lo *.la *.slo
|
||||
|
||||
distclean: clean
|
||||
|
||||
clean: clean-build clean-install clean-mlogc
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
###########################################################################
|
||||
### You Will need to modify the following variables for your system
|
||||
###########################################################################
|
||||
###########################################################################
|
||||
|
||||
# Path to Apache httpd installation
|
||||
BASE = C:\Apache2
|
||||
|
||||
# Paths to required libraries
|
||||
PCRE = C:\work\pcre-7.0-lib
|
||||
CURL = C:\work\libcurl-7.19.3-win32-ssl-msvc
|
||||
|
||||
# Linking libraries
|
||||
LIBS = $(BASE)\lib\libapr-1.lib \
|
||||
$(BASE)\lib\libaprutil-1.lib \
|
||||
$(PCRE)\lib\pcre.lib \
|
||||
$(CURL)\lib\Release\curllib.lib \
|
||||
wsock32.lib
|
||||
|
||||
###########################################################################
|
||||
###########################################################################
|
||||
|
||||
CC = cL
|
||||
|
||||
MT = mt
|
||||
|
||||
DEFS = /nologo /O2 /W3 -DWIN32 -DWINNT -Dinline=APR_INLINE -D_CONSOLE
|
||||
|
||||
EXE = mlogc.exe
|
||||
|
||||
INCLUDES = -I. -I.. \
|
||||
-I$(PCRE)\include -I$(PCRE) \
|
||||
-I$(CURL)\include -I$(CURL) \
|
||||
-I$(BASE)\include
|
||||
|
||||
CFLAGS= -MT $(INCLUDES) $(DEFS)
|
||||
|
||||
LDFLAGS =
|
||||
|
||||
OBJS = mlogc.obj
|
||||
|
||||
all: $(EXE)
|
||||
|
||||
.c.obj:
|
||||
$(CC) $(CFLAGS) -c $< -Fo$@
|
||||
|
||||
.cpp.obj:
|
||||
$(CC) $(CFLAGS) -c $< -Fo$@
|
||||
|
||||
$(EXE): $(OBJS)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) /link /NODEFAULTLIB:MSVCRT.lib /subsystem:console
|
||||
|
||||
install: $(EXE)
|
||||
copy $(EXE) $(BASE)\bin
|
||||
|
||||
clean:
|
||||
del $(OBJS) $(EXE) *.dll *.lib *.pdb *.idb *.ilk *.exp *.res *.rc *.bin *.manifest
|
||||
@@ -1,151 +0,0 @@
|
||||
#!@PERL@
|
||||
#
|
||||
# ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
# Copyright (c) 2004-2009 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
#
|
||||
# This product is released under the terms of the General Public Licence,
|
||||
# version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
# distribution) which contains the complete text of the licence.
|
||||
#
|
||||
# There are special exceptions to the terms and conditions of the GPL
|
||||
# as it is applied to this software. View the full text of the exception in
|
||||
# file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
# distribution.
|
||||
#
|
||||
# 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 support@trustwave.com.
|
||||
#
|
||||
|
||||
use strict;
|
||||
use File::Find qw(find);
|
||||
use File::Spec::Functions qw(catfile);
|
||||
use Sys::Hostname qw(hostname);
|
||||
use Digest::MD5 qw(md5_hex);
|
||||
|
||||
my $ROOTDIR = $ARGV[0] || '';
|
||||
my $MLOGC = $ARGV[1] || '';
|
||||
my $MLOGCCONF = $ARGV[2] || '';
|
||||
my @AUDIT = ();
|
||||
|
||||
if ($ROOTDIR eq '' or ! -e $MLOGC or ! -e $MLOGCCONF) {
|
||||
printf STDERR "\nUsage: $0 <rootdir> </path/to/mlogc> <mlogc_config>\n\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
open(MLOGC, "|$MLOGC -f $MLOGCCONF") or die "ERROR: could not open '$MLOGC' - $!\n";
|
||||
|
||||
find(
|
||||
{
|
||||
wanted => sub {
|
||||
my($fn,$dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size);
|
||||
|
||||
(($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size) = stat($_)) &&
|
||||
-f _ &&
|
||||
/^\d{8}-\d+-\w{24}$/s
|
||||
&& (($fn = $File::Find::name) =~ s/^\Q$ROOTDIR\E//)
|
||||
&& push(@AUDIT, [$fn, $size]);
|
||||
},
|
||||
follow => 1,
|
||||
},
|
||||
$ROOTDIR
|
||||
);
|
||||
|
||||
for my $audit (@AUDIT) {
|
||||
my $fn = $audit->[0];
|
||||
my $line = "";
|
||||
my $err = 0;
|
||||
my $ln = 0;
|
||||
my $sln = 0;
|
||||
my $sect = "";
|
||||
my $data = "";
|
||||
my %data = (
|
||||
hostname => hostname(),
|
||||
remote_addr => "-",
|
||||
remote_user => "-",
|
||||
local_user => "-",
|
||||
logtime => "-",
|
||||
request => "-",
|
||||
response_status => "-",
|
||||
bytes_sent => "-",
|
||||
referer => "-",
|
||||
user_agent => "-",
|
||||
uniqueid => "-",
|
||||
sessionid => "-",
|
||||
audit_file => $fn,
|
||||
extra => "0",
|
||||
audit_size => $audit->[1],
|
||||
md5 => "-",
|
||||
);
|
||||
|
||||
### Parse the audit file in an attempt to recreate the original log line
|
||||
open (AUDIT, "<".catfile($ROOTDIR,$fn)) or $err = 1;
|
||||
if ($err == 1) {
|
||||
print STDERR "ERROR: could not open '$fn' - $!\n";
|
||||
next;
|
||||
}
|
||||
|
||||
while($line = <AUDIT>) {
|
||||
$data .= $line;
|
||||
chop $line;
|
||||
$ln++;
|
||||
$sln++;
|
||||
if ($line =~ m%^--[0-9A-Fa-f]{8}-([A-Z])--$%) {
|
||||
$sect = $1;
|
||||
$sln = 0;
|
||||
next;
|
||||
};
|
||||
if ($sect eq 'A') {
|
||||
if ($line =~ m%^(\[[-\d/: a-zA-Z]{27}\]) (\S+) (\S+) (\d+) (\S+) (\d+)%) {
|
||||
$data{logtime} = $1;
|
||||
$data{uniqueid} = $2;
|
||||
$data{remote_addr} = $3;
|
||||
}
|
||||
next;
|
||||
}
|
||||
elsif ($sect eq 'B') {
|
||||
if ($sln == 1) {
|
||||
$data{request} = $line;
|
||||
}
|
||||
elsif ($line =~ m%^User=Agent: (.*)%i) {
|
||||
$data{user_agent} = $1;
|
||||
}
|
||||
elsif ($line =~ m%^Referer: (.*)%i) {
|
||||
$data{referer} = $1;
|
||||
}
|
||||
next;
|
||||
}
|
||||
elsif ($sect eq 'F') {
|
||||
if ($sln == 1 and $line =~ m%^\S+ (\d{3})\D?.*%) {
|
||||
$data{response_status} = $1;
|
||||
}
|
||||
elsif ($line =~ m%^Content-Length: (\d+)%i) {
|
||||
$data{bytes_sent} = $1;
|
||||
}
|
||||
next;
|
||||
}
|
||||
}
|
||||
$data{md5} = md5_hex($data);
|
||||
|
||||
printf MLOGC (
|
||||
"%s %s %s %s %s \"%s\" %s %s \"%s\" \"%s\" %s \"%s\" %s %s %s md5:%s\n",
|
||||
$data{hostname},
|
||||
$data{remote_addr},
|
||||
$data{remote_user},
|
||||
$data{local_user},
|
||||
$data{logtime},
|
||||
$data{request},
|
||||
$data{response_status},
|
||||
$data{bytes_sent},
|
||||
$data{referer},
|
||||
$data{user_agent},
|
||||
$data{uniqueid},
|
||||
$data{sessionid},
|
||||
$data{audit_file},
|
||||
$data{extra},
|
||||
$data{audit_size},
|
||||
$data{md5},
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
##########################################################################
|
||||
# Required configuration
|
||||
# At a minimum, the items in this section will need to be adjusted to
|
||||
# fit your environment. The remaining options are optional.
|
||||
##########################################################################
|
||||
|
||||
# Points to the root of the installation. All relative
|
||||
# paths will be resolved with the help of this path.
|
||||
CollectorRoot "/var/log/mlogc"
|
||||
|
||||
# ModSecurity Console receiving URI. You can change the host
|
||||
# and the port parts but leave everything else as is.
|
||||
ConsoleURI "https://CONSOLE_IP_ADDRESS:8888/rpc/auditLogReceiver"
|
||||
|
||||
# Sensor credentials
|
||||
SensorUsername "SENSOR_USERNAME"
|
||||
SensorPassword "SENSOR_PASSWORD"
|
||||
|
||||
# Base directory where the audit logs are stored. This can be specified
|
||||
# as a path relative to the CollectorRoot, or a full path.
|
||||
LogStorageDir "data"
|
||||
|
||||
# Transaction log will contain the information on all log collector
|
||||
# activities that happen between checkpoints. The transaction log
|
||||
# is used to recover data in case of a crash (or if Apache kills
|
||||
# the process).
|
||||
TransactionLog "mlogc-transaction.log"
|
||||
|
||||
# The file where the pending audit log entry data is kept. This file
|
||||
# is updated on every checkpoint.
|
||||
QueuePath "mlogc-queue.log"
|
||||
|
||||
# The location of the error log.
|
||||
ErrorLog "mlogc-error.log"
|
||||
|
||||
# The location of the lock file.
|
||||
LockFile "mlogc.lck"
|
||||
|
||||
# Keep audit log entries after sending? (0=false 1=true)
|
||||
# NOTE: This is required to be set in SecAuditLog mlogc config if you
|
||||
# are going to use a secondary console via SecAuditLog2.
|
||||
KeepEntries 0
|
||||
|
||||
|
||||
##########################################################################
|
||||
# Optional configuration
|
||||
##########################################################################
|
||||
|
||||
# The error log level controls how much detail there
|
||||
# will be in the error log. The levels are as follows:
|
||||
# 0 - NONE
|
||||
# 1 - ERROR
|
||||
# 2 - WARNING
|
||||
# 3 - NOTICE
|
||||
# 4 - DEBUG
|
||||
# 5 - DEBUG2
|
||||
#
|
||||
ErrorLogLevel 3
|
||||
|
||||
# How many concurrent connections to the server
|
||||
# are we allowed to open at the same time? Log collector uses
|
||||
# multiple connections in order to speed up audit log transfer.
|
||||
# This is especially needed when the communication takes place
|
||||
# over a slow link (e.g. not over a LAN).
|
||||
MaxConnections 10
|
||||
|
||||
# How many requests a worker will process before recycling itself.
|
||||
# This is to help prevent problems due to any memory leaks that may
|
||||
# exists. If this is set to 0, then no maximum is imposed. The default
|
||||
# is 1000 requests per worker (the number of workers is controlled by the
|
||||
# MaxConnections limit).
|
||||
MaxWorkerRequests 1000
|
||||
|
||||
# The time each connection will sit idle before being reused,
|
||||
# in milliseconds. Increase if you don't want ModSecurity Console
|
||||
# to be hit with too many log collector requests.
|
||||
TransactionDelay 50
|
||||
|
||||
# The time to wait before initialization on startup in milliseconds.
|
||||
# Increase if mlogc is starting faster then termination when the
|
||||
# sensor is reloaded.
|
||||
StartupDelay 5000
|
||||
|
||||
# How often is the pending audit log entry data going to be written
|
||||
# to a file. The default is 15 seconds.
|
||||
CheckpointInterval 15
|
||||
|
||||
# If the server fails all threads will back down until the
|
||||
# problem is sorted. The management thread will periodically
|
||||
# launch a thread to test the server. The default is to test
|
||||
# once in 60 seconds.
|
||||
ServerErrorTimeout 60
|
||||
|
||||
# The following two parameters are not used yet, but
|
||||
# reserved for future expansion.
|
||||
# KeepAlive 150
|
||||
# KeepAliveTimeout 300
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,142 +0,0 @@
|
||||
/* mod_security2_config.h.in. Generated from configure.in by autoheader. */
|
||||
|
||||
/* Define to 1 if you have the `atexit' function. */
|
||||
#undef HAVE_ATEXIT
|
||||
|
||||
/* Define to 1 if you have the `fchmod' function. */
|
||||
#undef HAVE_FCHMOD
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#undef HAVE_FCNTL_H
|
||||
|
||||
/* Define to 1 if you have the `getcwd' function. */
|
||||
#undef HAVE_GETCWD
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the <limits.h> header file. */
|
||||
#undef HAVE_LIMITS_H
|
||||
|
||||
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
|
||||
to 0 otherwise. */
|
||||
#undef HAVE_MALLOC
|
||||
|
||||
/* Define to 1 if you have the `memmove' function. */
|
||||
#undef HAVE_MEMMOVE
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the `memset' function. */
|
||||
#undef HAVE_MEMSET
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the `strcasecmp' function. */
|
||||
#undef HAVE_STRCASECMP
|
||||
|
||||
/* Define to 1 if you have the `strchr' function. */
|
||||
#undef HAVE_STRCHR
|
||||
|
||||
/* Define to 1 if you have the `strdup' function. */
|
||||
#undef HAVE_STRDUP
|
||||
|
||||
/* Define to 1 if you have the `strerror' function. */
|
||||
#undef HAVE_STRERROR
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the `strncasecmp' function. */
|
||||
#undef HAVE_STRNCASECMP
|
||||
|
||||
/* Define to 1 if you have the `strrchr' function. */
|
||||
#undef HAVE_STRRCHR
|
||||
|
||||
/* Define to 1 if you have the `strstr' function. */
|
||||
#undef HAVE_STRSTR
|
||||
|
||||
/* Define to 1 if you have the `strtol' function. */
|
||||
#undef HAVE_STRTOL
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
|
||||
#undef TM_IN_SYS_TIME
|
||||
|
||||
/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
|
||||
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||
#define below would cause a syntax error. */
|
||||
#undef _UINT8_T
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
#undef const
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef __cplusplus
|
||||
#undef inline
|
||||
#endif
|
||||
|
||||
/* Define to rpl_malloc if the replacement function should be used. */
|
||||
#undef malloc
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
#undef pid_t
|
||||
|
||||
/* Define to the equivalent of the C99 'restrict' keyword, or to
|
||||
nothing if this is not supported. Do not define if restrict is
|
||||
supported directly. */
|
||||
#undef restrict
|
||||
/* Work around a bug in Sun C++: it does not support _Restrict or
|
||||
__restrict__, even though the corresponding Sun C compiler ends up with
|
||||
"#define restrict _Restrict" or "#define restrict __restrict__" in the
|
||||
previous line. Perhaps some future version of Sun C++ will work with
|
||||
restrict; if so, hopefully it defines __RESTRICT like Sun C does. */
|
||||
#if defined __SUNPRO_CC && !defined __RESTRICT
|
||||
# define _Restrict
|
||||
# define __restrict__
|
||||
#endif
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
#undef size_t
|
||||
|
||||
/* Define to the type of an unsigned integer type of width exactly 8 bits if
|
||||
such a type exists and the standard includes do not define it. */
|
||||
#undef uint8_t
|
||||
@@ -1 +0,0 @@
|
||||
/* This file is left empty for building on Windows. */
|
||||
@@ -1,629 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "apr_global_mutex.h"
|
||||
|
||||
#include "modsecurity.h"
|
||||
#include "msc_parsers.h"
|
||||
#include "msc_util.h"
|
||||
#include "msc_xml.h"
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef __SET_MUTEX_PERMS
|
||||
rc = unixd_set_global_mutex_perms(msce->auditlog_lock);
|
||||
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
|
||||
|
||||
rc = apr_global_mutex_create(&msce->geo_lock, NULL, APR_LOCK_DEFAULT, mp);
|
||||
if (rc != APR_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef __SET_MUTEX_PERMS
|
||||
rc = unixd_set_global_mutex_perms(msce->geo_lock);
|
||||
if (rc != APR_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
#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 apr_status_t modsecurity_tx_cleanup(void *data) {
|
||||
modsec_rec *msr = (modsec_rec *)data;
|
||||
const apr_array_header_t *arr;
|
||||
apr_table_entry_t *te;
|
||||
int collect_garbage = 0;
|
||||
int i;
|
||||
char *my_error_msg = NULL;
|
||||
|
||||
if (msr == NULL) return APR_SUCCESS;
|
||||
|
||||
if (rand() < RAND_MAX/100) {
|
||||
collect_garbage = 1;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
if (collect_garbage) {
|
||||
collections_remove_stale(msr, te[i].key);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
apr_status_t modsecurity_tx_init(modsec_rec *msr) {
|
||||
const char *s = NULL;
|
||||
const apr_array_header_t *arr;
|
||||
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)&&(strstr(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;
|
||||
}
|
||||
}
|
||||
|
||||
msr->arguments_to_sanitise = apr_table_make(msr->mp, 16);
|
||||
if (msr->arguments_to_sanitise == NULL) return -1;
|
||||
msr->request_headers_to_sanitise = apr_table_make(msr->mp, 16);
|
||||
if (msr->request_headers_to_sanitise == NULL) return -1;
|
||||
msr->response_headers_to_sanitise = apr_table_make(msr->mp, 16);
|
||||
if (msr->response_headers_to_sanitise == NULL) return -1;
|
||||
|
||||
/* Initialise cookies */
|
||||
msr->request_cookies = apr_table_make(msr->mp, 16);
|
||||
if (msr->request_cookies == NULL) return -1;
|
||||
|
||||
/* 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) {
|
||||
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;
|
||||
|
||||
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) {
|
||||
msr_log(msr, 4, "Starting phase REQUEST_HEADERS.");
|
||||
|
||||
if (msr->txcfg->ruleset != NULL) {
|
||||
return msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static apr_status_t modsecurity_process_phase_request_body(modsec_rec *msr) {
|
||||
if ((msr->allow_scope == ACTION_ALLOW_REQUEST)||(msr->allow_scope == ACTION_ALLOW)) {
|
||||
msr_log(msr, 4, "Skipping phase REQUEST_BODY (allow used).");
|
||||
return 0;
|
||||
} else {
|
||||
msr_log(msr, 4, "Starting phase REQUEST_BODY.");
|
||||
}
|
||||
|
||||
if (msr->txcfg->ruleset != NULL) {
|
||||
return msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static apr_status_t modsecurity_process_phase_response_headers(modsec_rec *msr) {
|
||||
if (msr->allow_scope == ACTION_ALLOW) {
|
||||
msr_log(msr, 4, "Skipping phase RESPONSE_HEADERS (allow used).");
|
||||
return 0;
|
||||
} else {
|
||||
msr_log(msr, 4, "Starting phase RESPONSE_HEADERS.");
|
||||
}
|
||||
|
||||
if (msr->txcfg->ruleset != NULL) {
|
||||
return msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static apr_status_t modsecurity_process_phase_response_body(modsec_rec *msr) {
|
||||
if (msr->allow_scope == ACTION_ALLOW) {
|
||||
msr_log(msr, 4, "Skipping phase RESPONSE_BODY (allow used).");
|
||||
return 0;
|
||||
} else {
|
||||
msr_log(msr, 4, "Starting phase RESPONSE_BODY.");
|
||||
}
|
||||
|
||||
if (msr->txcfg->ruleset != NULL) {
|
||||
return msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static apr_status_t modsecurity_process_phase_logging(modsec_rec *msr) {
|
||||
msr_log(msr, 4, "Starting phase LOGGING.");
|
||||
|
||||
if (msr->txcfg->ruleset != NULL) {
|
||||
msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
|
||||
}
|
||||
|
||||
/* 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 :
|
||||
msr_log(msr, 4, "Audit log: Not configured to run for this request.");
|
||||
return DECLINED;
|
||||
break;
|
||||
|
||||
case AUDITLOG_RELEVANT :
|
||||
if (msr->is_relevant == 0) {
|
||||
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 */
|
||||
msr_log(msr, 4, "Audit log: Logging this transaction.");
|
||||
|
||||
sec_audit_logger(msr);
|
||||
|
||||
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)) {
|
||||
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) {
|
||||
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=<no change>", cn, rec->hits, key, rec->num, rec->path);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
apr_table_clear(tab);
|
||||
apr_hash_set(msr->tcache, key, klen, NULL);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,572 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#ifndef _MODSECURITY_H_
|
||||
#define _MODSECURITY_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
typedef struct rule_exception rule_exception;
|
||||
typedef struct modsec_rec modsec_rec;
|
||||
typedef struct directory_config directory_config;
|
||||
typedef struct error_message error_message;
|
||||
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;
|
||||
|
||||
#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 "re.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"
|
||||
|
||||
#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 SECACTION_TARGETS "REMOTE_ADDR"
|
||||
#define SECACTION_ARGS "@unconditionalMatch"
|
||||
|
||||
#define SECMARKER_TARGETS "REMOTE_ADDR"
|
||||
#define SECMARKER_ARGS "@noMatch"
|
||||
#define SECMARKER_BASE_ACTIONS "t:none,pass,id:"
|
||||
|
||||
#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 <direct.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#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 *real_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;
|
||||
|
||||
#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 MODSEC_DISABLED 0
|
||||
#define MODSEC_DETECTION_ONLY 1
|
||||
#define MODSEC_ENABLED 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 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_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;
|
||||
|
||||
/* 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_sanitise;
|
||||
apr_table_t *request_headers_to_sanitise;
|
||||
apr_table_t *response_headers_to_sanitise;
|
||||
apr_table_t *request_cookies;
|
||||
|
||||
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;
|
||||
|
||||
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_checkpoint_1;
|
||||
apr_time_t time_checkpoint_2;
|
||||
apr_time_t time_checkpoint_3;
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
||||
struct directory_config {
|
||||
apr_pool_t *mp;
|
||||
|
||||
msre_ruleset *ruleset;
|
||||
|
||||
int is_enabled;
|
||||
int reqbody_access;
|
||||
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;
|
||||
|
||||
const char *debuglog_name;
|
||||
int debuglog_level;
|
||||
apr_file_t *debuglog_fd;
|
||||
|
||||
int cookie_format;
|
||||
int argument_separator;
|
||||
|
||||
int rule_inheritance;
|
||||
apr_array_header_t *rule_exceptions;
|
||||
|
||||
|
||||
/* -- Audit log -- */
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Content injection. */
|
||||
int content_injection_enabled;
|
||||
|
||||
/* PDF XSS Protection. */
|
||||
int pdfp_enabled;
|
||||
const char *pdfp_secret;
|
||||
int pdfp_timeout;
|
||||
const char *pdfp_token_name;
|
||||
int pdfp_only_get;
|
||||
int pdfp_method;
|
||||
|
||||
/* Geo Lookup */
|
||||
geo_db *geo;
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
||||
struct error_message {
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
/* 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_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
|
||||
@@ -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 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 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
|
||||
@@ -1,519 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#include "msc_geo.h"
|
||||
|
||||
|
||||
/* -- Lookup Tables -- */
|
||||
|
||||
static const char *geo_country_code[GEO_COUNTRY_LAST + 1] = {
|
||||
"--",
|
||||
"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] = {
|
||||
"--",
|
||||
"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 *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] = {
|
||||
"--",
|
||||
"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"
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
apr_size_t nbytes;
|
||||
apr_off_t offset;
|
||||
unsigned char buf[3];
|
||||
int i, j;
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
offset = -3;
|
||||
apr_file_seek(geo->db, APR_END, &offset);
|
||||
/* TODO check offset */
|
||||
|
||||
/* Defaults */
|
||||
geo->dbtype = GEO_COUNTRY_DATABASE;
|
||||
geo->ctry_offset = GEO_COUNTRY_OFFSET;
|
||||
|
||||
for (i = 0; i < GEO_STRUCT_INFO_MAX_SIZE; i++) {
|
||||
memset(buf, 0, 3);
|
||||
rc = apr_file_read_full(geo->db, &buf, 3, &nbytes);
|
||||
#ifdef DEBUG_CONF
|
||||
fprintf(stderr, "GEO: read 0x%02x%02x%02x\n", buf[0], buf[1], buf[2]);
|
||||
#endif
|
||||
if ((rc != APR_SUCCESS) || (nbytes != 3)) {
|
||||
*error_msg = apr_psprintf(mp, "Could not read from geo database \"%s\" (%" APR_SIZE_T_FMT "/3 bytes read): %s", geo->dbfn, nbytes, apr_strerror(rc, errstr, 1024));
|
||||
return -1;
|
||||
}
|
||||
if ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff)) {
|
||||
#ifdef DEBUG_CONF
|
||||
fprintf(stderr, "GEO: Found DB info marker at offset 0x%08x\n", (unsigned int)offset);
|
||||
#endif
|
||||
memset(buf, 0, 3);
|
||||
rc = apr_file_read_full(geo->db, &buf, 1, &nbytes);
|
||||
/* TODO: check rc */
|
||||
geo->dbtype = (int)buf[0];
|
||||
|
||||
/* Backwards compat */
|
||||
if (geo->dbtype >= 106) {
|
||||
geo->dbtype -= 105;
|
||||
}
|
||||
#ifdef DEBUG_CONF
|
||||
fprintf(stderr, "GEO: DB type %d\n", geo->dbtype);
|
||||
#endif
|
||||
|
||||
/* If a cities DB, then get country offset */
|
||||
if ((geo->dbtype == GEO_CITY_DATABASE_0) || (geo->dbtype == GEO_CITY_DATABASE_1)) {
|
||||
memset(buf, 0, 3);
|
||||
rc = apr_file_read_full(geo->db, &buf, 3, &nbytes);
|
||||
if ((rc != APR_SUCCESS) || (nbytes != 3)) {
|
||||
*error_msg = apr_psprintf(mp, "Could not read geo database \"%s\" country offset (%" APR_SIZE_T_FMT "/3 bytes read): %s", geo->dbfn, nbytes, apr_strerror(rc, errstr, 1024));
|
||||
return -1;
|
||||
}
|
||||
#ifdef DEBUG_CONF
|
||||
fprintf(stderr, "GEO: read 0x%02x%02x%02x\n", buf[0], buf[1], buf[2]);
|
||||
#endif
|
||||
geo->ctry_offset = 0;
|
||||
for (j = 0; j < 3; j++) {
|
||||
geo->ctry_offset += (buf[j] << (j * 8));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CONF
|
||||
fprintf(stderr, "GEO: Country offset 0x%08x\n", geo->ctry_offset);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* Backup a byte from where we started */
|
||||
offset = -4;
|
||||
apr_file_seek(geo->db, APR_CUR, &offset);
|
||||
#ifdef DEBUG_CONF
|
||||
fprintf(stderr, "GEO: DB offset 0x%08x\n", (unsigned int)offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (geo->dbtype != GEO_COUNTRY_DATABASE) {
|
||||
*error_msg = apr_psprintf(mp, "Unknown database format");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CONF
|
||||
fprintf(stderr, "GEO: DB type %d\n", geo->dbtype);
|
||||
#endif
|
||||
|
||||
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).", targetip, 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[0] +
|
||||
(buf[1] << 8) +
|
||||
(buf[2] << 16);
|
||||
}
|
||||
/* Right */
|
||||
else {
|
||||
rec_val = buf[3] +
|
||||
(buf[4] << 8) +
|
||||
(buf[5] << 16);
|
||||
}
|
||||
|
||||
/* If we are past the country offset, then we are done */
|
||||
if (rec_val >= geo->ctry_offset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the resources used for Geo lookups
|
||||
*/
|
||||
apr_status_t geo_cleanup(modsec_rec *msr)
|
||||
{
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#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
|
||||
|
||||
|
||||
typedef struct geo_rec geo_rec;
|
||||
typedef struct geo_db geo_db;
|
||||
|
||||
#include <apr_file_io.h>
|
||||
#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);
|
||||
|
||||
apr_status_t DSOLOCAL geo_cleanup(modsec_rec *msr);
|
||||
|
||||
#endif
|
||||
@@ -1,983 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "mod_security2_config.h"
|
||||
#include "re.h"
|
||||
#include "msc_logging.h"
|
||||
#include "httpd.h"
|
||||
#include "apr_strings.h"
|
||||
#include "apr_global_mutex.h"
|
||||
#include "msc_util.h"
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a log line in the vcombinedus format (see below).
|
||||
*/
|
||||
char *construct_log_vcombinedus(modsec_rec *msr) {
|
||||
const char *local_user, *remote_user;
|
||||
const char *referer, *user_agent, *uniqueid;
|
||||
const char *sessionid;
|
||||
|
||||
/* remote log name */
|
||||
if (msr->remote_user == NULL) remote_user = "-";
|
||||
else remote_user = msr->remote_user;
|
||||
|
||||
/* authenticated user */
|
||||
if (msr->local_user == NULL) local_user = "-";
|
||||
else local_user = msr->local_user;
|
||||
|
||||
/* unique id */
|
||||
uniqueid = msr->txid;
|
||||
if (uniqueid == NULL) uniqueid = "-";
|
||||
|
||||
/* referer */
|
||||
referer = "-";
|
||||
/* Logging Referer is a waste of space.
|
||||
referer = (char *)apr_table_get(msr->request_headers, "Referer");
|
||||
if (referer == NULL) referer = "-";
|
||||
*/
|
||||
|
||||
/* user agent */
|
||||
user_agent = "-";
|
||||
/* Logging User-Agent is a waste of space too.
|
||||
user_agent = (char *)apr_table_get(msr->request_headers, "User-Agent");
|
||||
if (user_agent == NULL) user_agent = "-";
|
||||
*/
|
||||
|
||||
/* sessionid */
|
||||
sessionid = (msr->sessionid == NULL ? "-" : msr->sessionid);
|
||||
|
||||
return apr_psprintf(msr->mp, "%s %s %s %s [%s] \"%s\" %u %" APR_OFF_T_FMT " \"%s\" \"%s\" %s \"%s\"",
|
||||
log_escape_nq(msr->mp, msr->hostname), msr->remote_addr, log_escape_nq(msr->mp, remote_user),
|
||||
log_escape_nq(msr->mp, local_user), current_logtime(msr->mp),
|
||||
((msr->request_line == NULL) ? "" : log_escape(msr->mp, msr->request_line)),
|
||||
msr->response_status, msr->bytes_sent, log_escape(msr->mp, referer),
|
||||
log_escape(msr->mp, user_agent), log_escape(msr->mp, uniqueid), sessionid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
unsigned long data = rand();
|
||||
/* 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 sanitise_request_line(modsec_rec *msr) {
|
||||
const apr_array_header_t *tarr;
|
||||
const apr_table_entry_t *telts;
|
||||
int i;
|
||||
char *qspos;
|
||||
|
||||
/* 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_sanitise);
|
||||
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 *p;
|
||||
int j;
|
||||
|
||||
/* 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 sanitise 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;
|
||||
}
|
||||
|
||||
/* Write over the value. */
|
||||
j = arg->value_origin_len;
|
||||
while((*p != '\0')&&(j--)) {
|
||||
*p++ = '*';
|
||||
}
|
||||
if (*p == '\0') {
|
||||
msr_log(msr, 1, "Unable to sanitise 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
char *str1 = NULL, *str2 = NULL, *text = NULL;
|
||||
const msre_rule *rule = NULL;
|
||||
apr_size_t nbytes, nbytes_written;
|
||||
unsigned char md5hash[APR_MD5_DIGESTSIZE];
|
||||
int was_limited = 0;
|
||||
int wrote_response_body = 0;
|
||||
char *entry_filename, *entry_basename;
|
||||
apr_status_t rc;
|
||||
int i, limit;
|
||||
|
||||
/* 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) {
|
||||
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));
|
||||
|
||||
sanitise_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;
|
||||
for (i = 0; i < arr->nelts; i++) {
|
||||
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val);
|
||||
/* Do we need to sanitise this request header? */
|
||||
if (apr_table_get(msr->request_headers_to_sanitise, te[i].key) != NULL) {
|
||||
/* Yes, sanitise it. */
|
||||
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 sanitise = 0; /* IMP1 Use constants for "sanitise" 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
|
||||
* sanitised 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
|
||||
* sanitise 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_sanitise);
|
||||
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;
|
||||
|
||||
sanitise = 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
|
||||
* sanitise 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 sanitise_offset = 0;
|
||||
unsigned int sanitise_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 sanitise. */
|
||||
while (sanitise > 1) {
|
||||
msc_arg **arg = NULL;
|
||||
|
||||
if (sanitise == 2) {
|
||||
/* Get the next argument from the stack. */
|
||||
arg = (msc_arg **)apr_array_pop(sorted_args);
|
||||
if (arg == NULL) sanitise = 0; /* We're done sanitising. */
|
||||
else {
|
||||
/* Continue with sanitation to process the
|
||||
* retrieved argument.
|
||||
*/
|
||||
sanitise = 1;
|
||||
sanitise_offset = (*arg)->value_origin_offset;
|
||||
sanitise_length = (*arg)->value_origin_len;
|
||||
}
|
||||
}
|
||||
|
||||
if (sanitise) {
|
||||
/* Check if the data we want to sanitise is
|
||||
* stored in the current chunk.
|
||||
*/
|
||||
if (chunk_offset + chunk->length > sanitise_offset) {
|
||||
unsigned int soff; /* data offset within chunk */
|
||||
unsigned int len; /* amount in this chunk to sanitise */
|
||||
|
||||
soff = sanitise_offset - chunk_offset;
|
||||
|
||||
if (soff + sanitise_length <= chunk->length) {
|
||||
/* The entire argument resides in the current chunk. */
|
||||
len = sanitise_length;
|
||||
sanitise = 2; /* Get another parameter to sanitise. */
|
||||
} else {
|
||||
/* Some work to do here but we'll need to seek
|
||||
* another chunk.
|
||||
*/
|
||||
len = chunk->length - soff;
|
||||
sanitise_offset += len;
|
||||
sanitise_length -= len;
|
||||
sanitise = 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 sanitised 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_sanitise(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;
|
||||
for (i = 0; i < arr->nelts; i++) {
|
||||
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val);
|
||||
/* Do we need to sanitise this response header? */
|
||||
if (apr_table_get(msr->response_headers_to_sanitise, te[i].key) != NULL) {
|
||||
/* Yes, sanitise it. */
|
||||
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
|
||||
}
|
||||
sec_auditlog_write(msr, text, strlen(text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 *em = (((error_message**)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));
|
||||
}
|
||||
|
||||
/* Processing times */
|
||||
if (msr->time_checkpoint_1 == 0) {
|
||||
text = apr_psprintf(msr->mp, "Stopwatch: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT
|
||||
" (- - -)\n", (msr->request_time), (now - msr->request_time));
|
||||
} else {
|
||||
char sw_str2[101] = "-";
|
||||
char sw_str3[101] = "-";
|
||||
|
||||
if (msr->time_checkpoint_2 != 0) {
|
||||
apr_snprintf(sw_str2, sizeof(sw_str2), "%" APR_TIME_T_FMT,
|
||||
(msr->time_checkpoint_2 - msr->request_time));
|
||||
}
|
||||
|
||||
if (msr->time_checkpoint_3 != 0) {
|
||||
apr_snprintf(sw_str3, sizeof(sw_str3), "%" APR_TIME_T_FMT,
|
||||
(msr->time_checkpoint_3 - msr->request_time));
|
||||
}
|
||||
|
||||
text = apr_psprintf(msr->mp, "Stopwatch: %" APR_TIME_T_FMT
|
||||
" %" APR_TIME_T_FMT " (%" APR_TIME_T_FMT
|
||||
"%s %s %s)\n",
|
||||
(msr->request_time), (now - msr->request_time),
|
||||
(msr->time_checkpoint_1 - msr->request_time),
|
||||
((msr->msc_reqbody_read == 0) ? "" : "*"),
|
||||
sw_str2, sw_str3
|
||||
);
|
||||
}
|
||||
|
||||
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_sanitise);
|
||||
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_sanitise);
|
||||
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_sanitise);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/* AUDITLOG_PART_UPLOADS */
|
||||
/* ENH: Implement */
|
||||
|
||||
|
||||
/* 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];
|
||||
text = apr_psprintf(msr->mp, "%s\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);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#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"
|
||||
|
||||
int DSOLOCAL is_valid_parts_specification(char *p);
|
||||
|
||||
char DSOLOCAL *construct_log_vcombinedus(modsec_rec *msr);
|
||||
|
||||
char DSOLOCAL *construct_log_vcombinedus_limited(modsec_rec *msr, int _limit, int *was_limited);
|
||||
|
||||
void DSOLOCAL sec_audit_logger(modsec_rec *msr);
|
||||
|
||||
#endif
|
||||
@@ -1,484 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#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;
|
||||
|
||||
return lua_load(L, dump_reader, &dumpr, script->name);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
char *lua_compile(msc_script **script, const char *filename, apr_pool_t *pool) {
|
||||
lua_State *L = NULL;
|
||||
msc_lua_dumpw_t dump;
|
||||
|
||||
/* Initialise state. */
|
||||
L = lua_open();
|
||||
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? */
|
||||
int i, n = lua_objlen(L, idx);
|
||||
|
||||
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;
|
||||
|
||||
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();
|
||||
|
||||
/* Create new state. */
|
||||
L = lua_open();
|
||||
|
||||
luaL_openlibs(L);
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
//lua_pushcfunction(L, l_setvar);
|
||||
//lua_setglobal(L, "mysetvar");
|
||||
|
||||
/* Register functions. */
|
||||
luaL_register(L, "m", mylib);
|
||||
|
||||
rc = lua_restore(L, script);
|
||||
if (rc) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Lua: Failed to restore script with %i.", rc);
|
||||
|
||||
if (msr->txcfg->debuglog_level >= 8) {
|
||||
msr_log(msr, 8, "Lua: Failed to restore script with: %d",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);
|
||||
lua_close(L);
|
||||
|
||||
/* 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 */
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#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 <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
#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);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* WITH_LUA */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#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_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_sanitise(modsec_rec *msr);
|
||||
|
||||
#endif
|
||||
@@ -1,346 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#include "msc_parsers.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
int parse_cookies_v0(modsec_rec *msr, char *_cookie_header,
|
||||
apr_table_t *cookies)
|
||||
{
|
||||
char *attr_name = NULL, *attr_value = NULL;
|
||||
char *cookie_header;
|
||||
char *saveptr = NULL;
|
||||
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;
|
||||
|
||||
p = apr_strtok(cookie_header, ";", &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++;
|
||||
}
|
||||
|
||||
p = apr_strtok(NULL, ";", &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);
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#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);
|
||||
|
||||
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
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#include "msc_pcre.h"
|
||||
#include "apr_strings.h"
|
||||
|
||||
/**
|
||||
* Releases the resources used by a single regular expression pattern.
|
||||
*/
|
||||
apr_status_t msc_pcre_cleanup(msc_regex_t *regex) {
|
||||
if (regex != NULL) {
|
||||
if (regex->pe != NULL) {
|
||||
free(regex->pe);
|
||||
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
|
||||
pe = pcre_study(regex->re, 0, &errptr);
|
||||
|
||||
/* Setup the pcre_extra record if pcre_study did not already do it */
|
||||
if (pe == NULL) {
|
||||
pe = malloc(sizeof(pcre_extra));
|
||||
if (pe == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memset(pe, 0, sizeof(pcre_extra));
|
||||
}
|
||||
#endif
|
||||
|
||||
#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
|
||||
#ifdef MODSEC_PCRE_MATCH_LIMIT
|
||||
#pragma message ( "This PCRE version does not support match limits! Upgrade to at least PCRE v6.5." )
|
||||
#endif /* MODSEC_PCRE_MATCH_LIMIT */
|
||||
#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
|
||||
#ifdef MODSEC_PCRE_MATCH_LIMIT_RECURSION
|
||||
#pragma message ( "This PCRE version does not support match recursion limits! Upgrade to at least PCRE v6.5." )
|
||||
#endif /* MODSEC_PCRE_MATCH_LIMIT_RECURSION */
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#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;
|
||||
};
|
||||
|
||||
apr_status_t DSOLOCAL msc_pcre_cleanup(msc_regex_t *regex);
|
||||
|
||||
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
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "msc_release.h"
|
||||
|
||||
modsec_build_type_rec modsec_build_type[] = {
|
||||
{ "-dev", 1 }, /* Development build */
|
||||
{ "-rc", 3 }, /* Release Candidate build */
|
||||
{ "", 9 }, /* Production build */
|
||||
{ "-tw", 9 }, /* Trustwave Holdings build */
|
||||
{ "-trunk", 9 }, /* Trunk build */
|
||||
{ NULL, -1 } /* terminator */
|
||||
};
|
||||
|
||||
int get_modsec_build_type(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; modsec_build_type[i].name != NULL; 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 */
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#ifndef _MSC_RELEASE_H_
|
||||
#define _MSC_RELEASE_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 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
|
||||
|
||||
typedef struct modsec_build_type_rec {
|
||||
const char * name;
|
||||
int val;
|
||||
} modsec_build_type_rec;
|
||||
extern DSOLOCAL modsec_build_type_rec modsec_build_type[];
|
||||
|
||||
#define MODSEC_VERSION_MAJOR "2"
|
||||
#define MODSEC_VERSION_MINOR "5"
|
||||
#define MODSEC_VERSION_MAINT "13"
|
||||
#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 */
|
||||
#define MODSEC_MODULE_NAME "ModSecurity for Apache"
|
||||
#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_ */
|
||||
@@ -1,760 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#include "modsecurity.h"
|
||||
#include "msc_parsers.h"
|
||||
|
||||
#define CHUNK_CAPACITY 8192
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/* 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;
|
||||
|
||||
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->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;
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
msr_log(msr, 2, "%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, "Reqest 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;
|
||||
}
|
||||
@@ -1,921 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#include <apr.h>
|
||||
#include <apr_getopt.h>
|
||||
|
||||
#include "modsecurity.h"
|
||||
#include "re.h"
|
||||
#include "pdf_protect.h"
|
||||
|
||||
#define ISHEX(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F')))
|
||||
|
||||
#define BUFLEN 32768
|
||||
|
||||
#define RESULT_SUCCESS 0
|
||||
#define RESULT_ERROR -1
|
||||
#define RESULT_MISMATCHED -2
|
||||
#define RESULT_WRONGSIZE -3
|
||||
#define RESULT_WRONGRET -4
|
||||
|
||||
#define DEFAULT_ACTION "phase:2,log,auditlog,pass"
|
||||
|
||||
#define CMDLINE_OPTS "t:n:p:P:r:I:D:Nh"
|
||||
|
||||
/* Types */
|
||||
typedef struct tfn_data_t tfn_data_t;
|
||||
typedef struct op_data_t op_data_t;
|
||||
typedef struct action_data_t action_data_t;
|
||||
|
||||
struct tfn_data_t {
|
||||
const char *name;
|
||||
const char *param;
|
||||
unsigned char *input;
|
||||
apr_size_t input_len;
|
||||
msre_tfn_metadata *metadata;
|
||||
};
|
||||
|
||||
struct op_data_t {
|
||||
const char *name;
|
||||
const char *param;
|
||||
unsigned char *input;
|
||||
apr_size_t input_len;
|
||||
msre_ruleset *ruleset;
|
||||
msre_rule *rule;
|
||||
msre_var *var;
|
||||
msre_op_metadata *metadata;
|
||||
};
|
||||
|
||||
struct action_data_t {
|
||||
const char *name;
|
||||
unsigned char *input;
|
||||
apr_size_t input_len;
|
||||
msre_ruleset *ruleset;
|
||||
msre_rule *rule;
|
||||
msre_var *var;
|
||||
msre_actionset *actionset;
|
||||
msre_action *action;
|
||||
};
|
||||
|
||||
|
||||
/* Globals */
|
||||
static int debuglog_level = 0;
|
||||
static char *test_name = NULL;
|
||||
static apr_pool_t *g_mp = NULL;
|
||||
static modsec_rec *g_msr = NULL;
|
||||
static unsigned char buf[BUFLEN];
|
||||
msc_engine *modsecurity = NULL;
|
||||
unsigned long int DSOLOCAL msc_pcre_match_limit = 0;
|
||||
unsigned long int DSOLOCAL msc_pcre_match_limit_recursion = 0;
|
||||
|
||||
/* Stubs */
|
||||
char *format_error_log_message(apr_pool_t *mp, error_message *em) {
|
||||
return "FAKE ERROR LOG MESSAGE";
|
||||
}
|
||||
|
||||
apr_status_t send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status) {
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
|
||||
int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char **output) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void msr_log(modsec_rec *msr, int level, const char *text, ...) {
|
||||
va_list ap;
|
||||
char str1[1024] = "";
|
||||
char str2[1256] = "";
|
||||
|
||||
if ((msr == NULL) || (level > msr->txcfg->debuglog_level)) {
|
||||
return;
|
||||
}
|
||||
if (msr->txcfg->debuglog_fd == NOT_SET_P) {
|
||||
if (apr_file_open(&msr->txcfg->debuglog_fd, msr->txcfg->debuglog_name, APR_READ|APR_WRITE|APR_CREATE|APR_APPEND|APR_BINARY, APR_OS_DEFAULT, g_mp) != APR_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to create unit test debug log \"%s\".\n", msr->txcfg->debuglog_name);
|
||||
msr->txcfg->debuglog_fd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
va_start(ap, text);
|
||||
if (msr->txcfg->debuglog_fd != NULL) {
|
||||
apr_size_t nbytes_written = 0;
|
||||
apr_vsnprintf(str1, sizeof(str1), text, ap);
|
||||
apr_snprintf(str2, sizeof(str2), "%lu: [%d] [%s] %s\n", (unsigned long)getpid(), level, test_name, str1);
|
||||
|
||||
apr_file_write_full(msr->txcfg->debuglog_fd, str2, strlen(str2), &nbytes_written);
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void msr_log_error(modsec_rec *msr, const char *text, ...) {
|
||||
va_list ap;
|
||||
int level = 3;
|
||||
char str1[1024] = "";
|
||||
char str2[1256] = "";
|
||||
|
||||
if ((msr == NULL) || (level > msr->txcfg->debuglog_level)) {
|
||||
return;
|
||||
}
|
||||
if (msr->txcfg->debuglog_fd == NOT_SET_P) {
|
||||
if (apr_file_open(&msr->txcfg->debuglog_fd, msr->txcfg->debuglog_name, APR_READ|APR_WRITE|APR_CREATE|APR_APPEND|APR_BINARY, APR_OS_DEFAULT, g_mp) != APR_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to create unit test debug log \"%s\".\n", msr->txcfg->debuglog_name);
|
||||
msr->txcfg->debuglog_fd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
va_start(ap, text);
|
||||
if (msr->txcfg->debuglog_fd != NULL) {
|
||||
apr_size_t nbytes_written = 0;
|
||||
apr_vsnprintf(str1, sizeof(str1), text, ap);
|
||||
apr_snprintf(str2, sizeof(str2), "%lu: [%d] [%s] %s\n", (unsigned long)getpid(), level, test_name, str1);
|
||||
|
||||
apr_file_write_full(msr->txcfg->debuglog_fd, str2, strlen(str2), &nbytes_written);
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void msr_log_warn(modsec_rec *msr, const char *text, ...) {
|
||||
va_list ap;
|
||||
int level = 4;
|
||||
char str1[1024] = "";
|
||||
char str2[1256] = "";
|
||||
|
||||
if ((msr == NULL) || (level > msr->txcfg->debuglog_level)) {
|
||||
return;
|
||||
}
|
||||
if (msr->txcfg->debuglog_fd == NOT_SET_P) {
|
||||
if (apr_file_open(&msr->txcfg->debuglog_fd, msr->txcfg->debuglog_name, APR_READ|APR_WRITE|APR_CREATE|APR_APPEND|APR_BINARY, APR_OS_DEFAULT, g_mp) != APR_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: failed to create unit test debug log \"%s\".\n", msr->txcfg->debuglog_name);
|
||||
msr->txcfg->debuglog_fd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
va_start(ap, text);
|
||||
if (msr->txcfg->debuglog_fd != NULL) {
|
||||
apr_size_t nbytes_written = 0;
|
||||
apr_vsnprintf(str1, sizeof(str1), text, ap);
|
||||
apr_snprintf(str2, sizeof(str2), "%lu: [%d] [%s] %s\n", (unsigned long)getpid(), level, test_name, str1);
|
||||
|
||||
apr_file_write_full(msr->txcfg->debuglog_fd, str2, strlen(str2), &nbytes_written);
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
const char *ap_get_remote_host(conn_rec *conn, void *dir_config, int type, int *str_is_ip) {
|
||||
return "FAKE-REMOTE-HOST";
|
||||
}
|
||||
|
||||
char *get_env_var(request_rec *r, char *name) {
|
||||
return "FAKE-ENV-VAR";
|
||||
}
|
||||
|
||||
apr_status_t unixd_set_global_mutex_perms(apr_global_mutex_t *gmutex) {
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
|
||||
apr_status_t unixd_set_proc_mutex_perms(apr_proc_mutex_t *pmutex) {
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* Escaping functions */
|
||||
|
||||
static unsigned char hex2dec(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;
|
||||
}
|
||||
|
||||
static unsigned char *unescape_inplace(unsigned char *str, apr_size_t *len)
|
||||
{
|
||||
apr_size_t i, j;
|
||||
for (i = j = 0; i < *len; j++) {
|
||||
if ((str[i] == '\\') && (i + 3 < *len) && (str[i + 1] == 'x') && ISHEX(str[i + 2]) && ISHEX(str[i + 3]) ) {
|
||||
str[j] = hex2dec(str + i + 2);
|
||||
i += 4;
|
||||
}
|
||||
else {
|
||||
str[j] = str[i++];
|
||||
}
|
||||
}
|
||||
*len = j;
|
||||
|
||||
while (j < i) {
|
||||
str[j++] = '\0';
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *escape(unsigned char *str, apr_size_t *len)
|
||||
{
|
||||
char *new = apr_pcalloc(g_mp, (*len * 4) + 1);
|
||||
apr_size_t i, j;
|
||||
for (i = j = 0; i < *len; i++) {
|
||||
if ((str[i] >= 0x20) && (str[i] <= 0x7e)) {
|
||||
new[j++] = str[i];
|
||||
}
|
||||
else {
|
||||
sprintf(new + j, "\\x%02x", str[i]);
|
||||
j += 4;
|
||||
}
|
||||
}
|
||||
*len = j;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/* Testing functions */
|
||||
|
||||
static int init_tfn(tfn_data_t *data, const char *name, unsigned char *input, apr_size_t input_len, char **errmsg) {
|
||||
*errmsg = NULL;
|
||||
|
||||
data->name = name;
|
||||
data->input = apr_pmemdup(g_mp, input, input_len);
|
||||
data->input_len = input_len;
|
||||
data->metadata = msre_engine_tfn_resolve(modsecurity->msre, name);
|
||||
if (data->metadata == NULL) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to fetch tfn \"%s\".", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_tfn(tfn_data_t *data, unsigned char **rval, apr_size_t *rval_len, char **errmsg)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
*errmsg = NULL;
|
||||
|
||||
/* Execute the tfn */
|
||||
rc = data->metadata->execute(g_mp, data->input, (long)(data->input_len), (char **)rval, (long *)rval_len);
|
||||
if (rc < 0) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to execute tfn \"%s\".", data->name);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int init_op(op_data_t *data, const char *name, const char *param, unsigned char *input, apr_size_t input_len, char **errmsg) {
|
||||
const char *args = apr_psprintf(g_mp, "@%s %s", name, param);
|
||||
char *conf_fn;
|
||||
int rc = -1;
|
||||
|
||||
*errmsg = NULL;
|
||||
|
||||
data->name = name;
|
||||
data->param = param;
|
||||
data->input = input;
|
||||
data->input_len = input_len;
|
||||
|
||||
if ( apr_filepath_merge(&conf_fn, NULL, "t/unit-test.conf", APR_FILEPATH_TRUENAME, g_mp) != APR_SUCCESS) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to build a conf filename.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Register UNIT_TEST variable */
|
||||
msre_engine_variable_register(modsecurity->msre,
|
||||
"UNIT_TEST",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
NULL,
|
||||
VAR_DONT_CACHE,
|
||||
PHASE_REQUEST_HEADERS
|
||||
);
|
||||
|
||||
/* Lookup the operator */
|
||||
data->metadata = msre_engine_op_resolve(modsecurity->msre, name);
|
||||
if (data->metadata == NULL) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to fetch op \"%s\".", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create a ruleset/rule */
|
||||
data->ruleset = msre_ruleset_create(modsecurity->msre, g_mp);
|
||||
if (data->ruleset == NULL) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to create ruleset for op \"%s\".", name);
|
||||
return -1;
|
||||
}
|
||||
data->rule = msre_rule_create(data->ruleset, RULE_TYPE_NORMAL, conf_fn, 1, "UNIT_TEST", args, DEFAULT_ACTION, errmsg);
|
||||
if (data->rule == NULL) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to create rule for op \"%s\": %s", name, *errmsg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create a fake variable */
|
||||
data->var = (msre_var *)apr_pcalloc(g_mp, sizeof(msre_var));
|
||||
data->var->name = "UNIT_TEST";
|
||||
data->var->value = apr_pstrmemdup(g_mp, (char *)input, input_len);
|
||||
data->var->value_len = input_len;
|
||||
data->var->metadata = msre_resolve_var(modsecurity->msre, data->var->name);
|
||||
if (data->var->metadata == NULL) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to resolve variable for op \"%s\": %s", name, data->var->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize the operator parameter */
|
||||
if (data->metadata->param_init != NULL) {
|
||||
rc = data->metadata->param_init(data->rule, errmsg);
|
||||
if (rc <= 0) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to init op \"%s\": %s", name, *errmsg);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_op(op_data_t *data, char **errmsg)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
*errmsg = NULL;
|
||||
|
||||
/* Execute the operator */
|
||||
if (data->metadata->execute != NULL) {
|
||||
rc = data->metadata->execute(g_msr, data->rule, data->var, errmsg);
|
||||
if (rc < 0) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to execute op \"%s\": %s", data->name, *errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int init_action(action_data_t *data, const char *name, const char *param, char **errmsg)
|
||||
{
|
||||
const char *action_string = NULL;
|
||||
char *conf_fn;
|
||||
|
||||
*errmsg = NULL;
|
||||
|
||||
if ((param == NULL) || (strcmp("", param) == 0)) {
|
||||
action_string = apr_psprintf(g_mp, "%s", name);
|
||||
}
|
||||
else {
|
||||
action_string = apr_psprintf(g_mp, "%s:%s", name, param);
|
||||
}
|
||||
if (action_string == NULL) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to build action string for action: \"%s\".", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( apr_filepath_merge(&conf_fn, NULL, "t/unit-test.conf", APR_FILEPATH_TRUENAME, g_mp) != APR_SUCCESS) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to build a conf filename.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Register UNIT_TEST variable */
|
||||
msre_engine_variable_register(modsecurity->msre,
|
||||
"UNIT_TEST",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
NULL,
|
||||
VAR_DONT_CACHE,
|
||||
PHASE_REQUEST_HEADERS
|
||||
);
|
||||
|
||||
/* Create a ruleset/rule */
|
||||
data->ruleset = msre_ruleset_create(modsecurity->msre, g_mp);
|
||||
if (data->ruleset == NULL) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to create ruleset for action \"%s\".", name);
|
||||
return -1;
|
||||
}
|
||||
data->rule = msre_rule_create(data->ruleset, RULE_TYPE_NORMAL, conf_fn, 1, "UNIT_TEST", "@unconditionalMatch", action_string, errmsg);
|
||||
if (data->rule == NULL) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to create rule for action \"%s\": %s", name, *errmsg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the actionset/action */
|
||||
data->actionset = data->rule->actionset;
|
||||
if (data->actionset == NULL) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to fetch actionset for action \"%s\"", name);
|
||||
return -1;
|
||||
}
|
||||
data->action = (msre_action *)apr_table_get(data->actionset->actions, name);
|
||||
if (data->action == NULL) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to fetch action for action \"%s\"", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_action(action_data_t *data, char **errmsg)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
*errmsg = NULL;
|
||||
|
||||
/* Execute the action */
|
||||
if (data->action->metadata->execute != NULL) {
|
||||
rc = data->action->metadata->execute(g_msr, g_mp, data->rule, data->action);
|
||||
if (rc < 0) {
|
||||
*errmsg = apr_psprintf(g_mp, "Failed to execute action \"%s\": %d", data->name, rc);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Initialization */
|
||||
static void init_msr(void)
|
||||
{
|
||||
directory_config *dcfg = NULL;
|
||||
request_rec *r = NULL;
|
||||
r = (request_rec *)apr_pcalloc(g_mp, sizeof(request_rec));
|
||||
|
||||
dcfg = (directory_config *)apr_pcalloc(g_mp, sizeof(directory_config));
|
||||
dcfg->is_enabled = 0;
|
||||
dcfg->reqbody_access = 0;
|
||||
dcfg->reqbody_buffering = 0;
|
||||
dcfg->reqbody_inmemory_limit = REQUEST_BODY_DEFAULT_INMEMORY_LIMIT;
|
||||
dcfg->reqbody_limit = REQUEST_BODY_DEFAULT_LIMIT;
|
||||
dcfg->reqbody_no_files_limit = REQUEST_BODY_NO_FILES_DEFAULT_LIMIT;
|
||||
dcfg->resbody_access = 0;
|
||||
dcfg->of_limit = RESPONSE_BODY_DEFAULT_LIMIT;
|
||||
dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT;
|
||||
dcfg->debuglog_fd = NOT_SET_P;
|
||||
dcfg->debuglog_name = "msc-test-debug.log";
|
||||
dcfg->debuglog_level = debuglog_level;
|
||||
dcfg->cookie_format = 0;
|
||||
dcfg->argument_separator = '&';
|
||||
dcfg->rule_inheritance = 0;
|
||||
dcfg->auditlog_flag = 0;
|
||||
dcfg->auditlog_type = AUDITLOG_SERIAL;
|
||||
dcfg->auditlog_fd = NULL;
|
||||
dcfg->auditlog2_fd = NULL;
|
||||
dcfg->auditlog_name = NULL;
|
||||
dcfg->auditlog2_name = NULL;
|
||||
dcfg->auditlog_storage_dir = NULL;
|
||||
dcfg->auditlog_parts = "ABCFHZ";
|
||||
dcfg->auditlog_relevant_regex = NULL;
|
||||
dcfg->tmp_dir = guess_tmp_dir(g_mp);
|
||||
dcfg->upload_dir = NULL;
|
||||
dcfg->upload_keep_files = KEEP_FILES_OFF;
|
||||
dcfg->upload_validates_files = 0;
|
||||
dcfg->data_dir = ".";
|
||||
dcfg->webappid = "default";
|
||||
dcfg->content_injection_enabled = 0;
|
||||
dcfg->pdfp_enabled = 0;
|
||||
dcfg->pdfp_secret = NULL;
|
||||
dcfg->pdfp_timeout = 10;
|
||||
dcfg->pdfp_token_name = "PDFPTOKEN";
|
||||
dcfg->pdfp_only_get = 1;
|
||||
dcfg->pdfp_method = PDF_PROTECT_METHOD_TOKEN_REDIRECTION;
|
||||
dcfg->geo = NULL;
|
||||
dcfg->cache_trans = MODSEC_CACHE_ENABLED;
|
||||
dcfg->cache_trans_min = 15;
|
||||
dcfg->cache_trans_max = 0;
|
||||
dcfg->request_encoding = NULL;
|
||||
|
||||
g_msr = (modsec_rec *)apr_pcalloc(g_mp, sizeof(modsec_rec));
|
||||
g_msr->modsecurity = modsecurity;
|
||||
g_msr->mp = g_mp;
|
||||
g_msr->r = r;
|
||||
g_msr->r_early = r;
|
||||
g_msr->request_time = apr_time_now();
|
||||
g_msr->dcfg1 = NULL;
|
||||
g_msr->usercfg = NULL;
|
||||
g_msr->txcfg = dcfg;
|
||||
g_msr->txid = "FAKE-TXID";
|
||||
g_msr->error_messages = NULL;
|
||||
g_msr->alerts = NULL;
|
||||
g_msr->server_software = "FAKE-SERVER-SOFTWARE";
|
||||
g_msr->local_addr = "127.0.0.1";
|
||||
g_msr->local_port = 80;
|
||||
g_msr->remote_addr = "127.0.0.1";
|
||||
g_msr->remote_port = 1080;
|
||||
g_msr->request_line = "GET /unit-tests HTTP/1.1";
|
||||
g_msr->request_uri = "http://localhost/unit-tests";
|
||||
g_msr->request_method = "GET";
|
||||
g_msr->query_string = "";
|
||||
g_msr->request_protocol = "HTTP/1.1";
|
||||
g_msr->request_headers = NULL;
|
||||
g_msr->hostname = "localhost";
|
||||
g_msr->msc_rule_mptmp = g_mp;
|
||||
g_msr->tx_vars = apr_table_make(g_mp, 1);
|
||||
g_msr->collections_original = apr_table_make(g_mp, 1);
|
||||
g_msr->collections = apr_table_make(g_mp, 1);
|
||||
g_msr->collections_dirty = apr_table_make(g_mp, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage text.
|
||||
*/
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "ModSecurity Unit Tester v%s\n", MODSEC_VERSION);
|
||||
fprintf(stderr, " Usage: msc_test [options]\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " Options:\n");
|
||||
fprintf(stderr, " -t Type (required)\n");
|
||||
fprintf(stderr, " -n Name (required)\n");
|
||||
fprintf(stderr, " -p Parameter (required)\n");
|
||||
fprintf(stderr, " -P Prerun (optional for actions)\n");
|
||||
fprintf(stderr, " -r Function return code (required for some types)\n");
|
||||
fprintf(stderr, " -I Iterations (default 1)\n");
|
||||
fprintf(stderr, " -D Debug log level (default 0)\n");
|
||||
fprintf(stderr, " -N No input on stdin.\n\n");
|
||||
fprintf(stderr, " -h This help\n\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Input is from stdin unless -N is used.\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Main */
|
||||
|
||||
int main(int argc, const char * const argv[])
|
||||
{
|
||||
apr_getopt_t *opt;
|
||||
apr_file_t *fd;
|
||||
apr_size_t nbytes = 0;
|
||||
const char *type = NULL;
|
||||
const char *name = NULL;
|
||||
unsigned char *param = NULL;
|
||||
unsigned char *prerun = NULL;
|
||||
const char *returnval = NULL;
|
||||
int iterations = 1;
|
||||
char *errmsg = NULL;
|
||||
unsigned char *out = NULL;
|
||||
apr_size_t param_len = 0;
|
||||
apr_size_t prerun_len = 0;
|
||||
apr_size_t out_len = 0;
|
||||
int noinput = 0;
|
||||
int rc = 0;
|
||||
int result = 0;
|
||||
int ec = 0;
|
||||
int i;
|
||||
apr_time_t T0 = 0;
|
||||
apr_time_t T1 = 0;
|
||||
tfn_data_t tfn_data;
|
||||
op_data_t op_data;
|
||||
action_data_t action_data;
|
||||
int ret = 0;
|
||||
|
||||
memset(&tfn_data, 0, sizeof(tfn_data_t));
|
||||
memset(&op_data, 0, sizeof(op_data_t));
|
||||
memset(&action_data, 0, sizeof(action_data_t));
|
||||
|
||||
apr_app_initialize(&argc, &argv, NULL);
|
||||
atexit(apr_terminate);
|
||||
|
||||
apr_pool_create(&g_mp, NULL);
|
||||
|
||||
rc = apr_getopt_init(&opt, g_mp, argc, argv);
|
||||
if (rc != APR_SUCCESS) {
|
||||
fprintf(stderr, "Failed to initialize.\n\n");
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
do {
|
||||
char ch;
|
||||
const char *val;
|
||||
rc = apr_getopt(opt, CMDLINE_OPTS, &ch, &val);
|
||||
switch (rc) {
|
||||
case APR_SUCCESS:
|
||||
switch (ch) {
|
||||
case 't':
|
||||
type = val;
|
||||
break;
|
||||
case 'n':
|
||||
name = val;
|
||||
break;
|
||||
case 'p':
|
||||
param_len = strlen(val);
|
||||
param = apr_pmemdup(g_mp, val, param_len + 1);
|
||||
unescape_inplace(param, ¶m_len);
|
||||
break;
|
||||
case 'P':
|
||||
prerun_len = strlen(val);
|
||||
prerun = apr_pmemdup(g_mp, val, prerun_len + 1);
|
||||
unescape_inplace(prerun, &prerun_len);
|
||||
break;
|
||||
case 'r':
|
||||
returnval = val;
|
||||
break;
|
||||
case 'I':
|
||||
iterations = atoi(val);
|
||||
break;
|
||||
case 'D':
|
||||
debuglog_level = atoi(val);
|
||||
break;
|
||||
case 'N':
|
||||
noinput = 1;
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
case APR_BADCH:
|
||||
case APR_BADARG:
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
} while (rc != APR_EOF);
|
||||
|
||||
rc = apr_getopt_init(&opt, g_mp, argc, argv);
|
||||
if (!type || !name || !param) {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
modsecurity = modsecurity_create(g_mp, MODSEC_OFFLINE);
|
||||
test_name = apr_psprintf(g_mp, "%s/%s", type, name);
|
||||
|
||||
if (noinput == 0) {
|
||||
if (apr_file_open_stdin(&fd, g_mp) != APR_SUCCESS) {
|
||||
fprintf(stderr, "Failed to open stdin\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Read in the input */
|
||||
nbytes = BUFLEN;
|
||||
memset(buf, 0, nbytes);
|
||||
rc = apr_file_read(fd, buf, &nbytes);
|
||||
if ((rc != APR_EOF) && (rc != APR_SUCCESS)) {
|
||||
fprintf(stderr, "Failed to read data\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (nbytes < 0) {
|
||||
fprintf(stderr, "Error reading data\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
apr_file_close(fd);
|
||||
}
|
||||
|
||||
if (strcmp("tfn", type) == 0) {
|
||||
ret = returnval ? atoi(returnval) : -8888;
|
||||
|
||||
rc = init_tfn(&tfn_data, name, buf, nbytes, &errmsg);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERROR: %s\n", errmsg);
|
||||
result = RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
else if (strcmp("op", type) == 0) {
|
||||
if (!returnval) {
|
||||
fprintf(stderr, "Return value required for type \"%s\"\n", type);
|
||||
exit(1);
|
||||
}
|
||||
ret = atoi(returnval);
|
||||
|
||||
init_msr();
|
||||
|
||||
rc = init_op(&op_data, name, (const char *)param, buf, nbytes, &errmsg);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERROR: %s\n", errmsg);
|
||||
result = RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
else if (strcmp("action", type) == 0) {
|
||||
if (!returnval) {
|
||||
fprintf(stderr, "Return value required for type \"%s\"\n", type);
|
||||
exit(1);
|
||||
}
|
||||
ret = atoi(returnval);
|
||||
|
||||
init_msr();
|
||||
|
||||
if (prerun) {
|
||||
action_data_t paction_data;
|
||||
char *pname = apr_pstrdup(g_mp, (const char *)prerun);
|
||||
char *pparam = NULL;
|
||||
|
||||
if ((pparam = strchr((const char *)pname, ':'))) {
|
||||
pparam[0] = '\0';
|
||||
pparam++;
|
||||
}
|
||||
|
||||
rc = init_action(&paction_data, pname, (const char *)pparam, &errmsg);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERROR: prerun - %s\n", errmsg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rc = test_action(&paction_data, &errmsg);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERROR: prerun - %s\n", errmsg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
rc = init_action(&action_data, name, (const char *)param, &errmsg);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERROR: %s\n", errmsg);
|
||||
result = RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (iterations > 1) {
|
||||
apr_time_clock_hires (g_mp);
|
||||
T0 = apr_time_now();
|
||||
}
|
||||
|
||||
for (i = 1; i <= iterations; i++) {
|
||||
#ifdef VERBOSE
|
||||
if (i % 100 == 0) {
|
||||
if (i == 100) {
|
||||
fprintf(stderr, "Iterations/100: .");
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, ".");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (strcmp("tfn", type) == 0) {
|
||||
/* Transformations */
|
||||
rc = test_tfn(&tfn_data, &out, &out_len, &errmsg);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERROR: %s\n", errmsg);
|
||||
result = RESULT_ERROR;
|
||||
}
|
||||
else if ((ret != -8888) && (rc != ret)) {
|
||||
fprintf(stderr, "Returned %d (expected %d)\n", rc, ret);
|
||||
result = RESULT_WRONGRET;
|
||||
}
|
||||
else if (param_len != out_len) {
|
||||
fprintf(stderr, "Length %" APR_SIZE_T_FMT " (expected %" APR_SIZE_T_FMT ")\n", out_len, param_len);
|
||||
result = RESULT_WRONGSIZE;
|
||||
}
|
||||
else {
|
||||
result = memcmp(param, out, param_len) ? RESULT_MISMATCHED : RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
if (result != RESULT_SUCCESS) {
|
||||
apr_size_t s0len = nbytes;
|
||||
const char *s0 = escape(buf, &s0len);
|
||||
apr_size_t s1len = out_len;
|
||||
const char *s1 = escape(out, &s1len);
|
||||
apr_size_t s2len = param_len;
|
||||
const char *s2 = escape(param, &s2len);
|
||||
|
||||
fprintf(stderr, " Input: '%s' len=%" APR_SIZE_T_FMT "\n"
|
||||
"Output: '%s' len=%" APR_SIZE_T_FMT "\n"
|
||||
"Expect: '%s' len=%" APR_SIZE_T_FMT "\n",
|
||||
s0, nbytes, s1, out_len, s2, param_len);
|
||||
ec = 1;
|
||||
}
|
||||
}
|
||||
else if (strcmp("op", type) == 0) {
|
||||
/* Operators */
|
||||
rc = test_op(&op_data, &errmsg);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERROR: %s\n", errmsg);
|
||||
result = RESULT_ERROR;
|
||||
}
|
||||
else if (rc != ret) {
|
||||
fprintf(stderr, "Returned %d (expected %d)\n", rc, ret);
|
||||
result = RESULT_WRONGRET;
|
||||
}
|
||||
else {
|
||||
result = RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
if (result != RESULT_SUCCESS) {
|
||||
apr_size_t s0len = nbytes;
|
||||
const char *s0 = escape(buf, &s0len);
|
||||
|
||||
fprintf(stderr, " Test: '@%s %s'\n"
|
||||
"Input: '%s' len=%" APR_SIZE_T_FMT "\n",
|
||||
name, param, s0, nbytes);
|
||||
ec = 1;
|
||||
}
|
||||
}
|
||||
else if (strcmp("action", type) == 0) {
|
||||
/* Actions */
|
||||
int n;
|
||||
const apr_array_header_t *arr;
|
||||
apr_table_entry_t *te;
|
||||
|
||||
rc = test_action(&action_data, &errmsg);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "ERROR: %s\n", errmsg);
|
||||
result = RESULT_ERROR;
|
||||
}
|
||||
else if (rc != ret) {
|
||||
fprintf(stderr, "Returned %d (expected %d)\n", rc, ret);
|
||||
result = RESULT_WRONGRET;
|
||||
}
|
||||
else {
|
||||
result = RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
if (result != RESULT_SUCCESS) {
|
||||
fprintf(stderr, " Test: '%s:%s'\n"
|
||||
"Prerun: '%s'\n",
|
||||
name, param, (prerun ? (const char *)prerun : ""));
|
||||
ec = 1;
|
||||
}
|
||||
|
||||
/* Store any collections that were initialized and changed */
|
||||
arr = apr_table_elts(g_msr->collections);
|
||||
te = (apr_table_entry_t *)arr->elts;
|
||||
for (n = 0; n < arr->nelts; n++) {
|
||||
apr_table_t *col = (apr_table_t *)te[n].val;
|
||||
// apr_table_t *orig_col = NULL;
|
||||
|
||||
if (g_msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(g_msr, 9, "Found loaded collection: %s", te[n].key);
|
||||
}
|
||||
/* Only store those collections that changed. */
|
||||
if (apr_table_get(g_msr->collections_dirty, te[n].key)) {
|
||||
int x = collection_store(g_msr, col);
|
||||
|
||||
if (g_msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(g_msr, 9, "Stored collection: %s (%d)", te[n].key, x);
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
/* Re-populate the original values with the new ones. */
|
||||
if ((orig_col = (apr_table_t *)apr_table_get(g_msr->collections_original, te[n].key)) != NULL) {
|
||||
const apr_array_header_t *orig_arr = apr_table_elts(orig_col);
|
||||
apr_table_entry_t *orig_te = (apr_table_entry_t *)orig_arr->elts;
|
||||
int m;
|
||||
|
||||
for (m = 0; m < orig_arr->nelts; m++) {
|
||||
msc_string *mstr = (msc_string *)apr_table_get(col, orig_te[m].key);
|
||||
|
||||
if (g_msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(g_msr, 9, "Updating original collection: %s.%s=%s", te[n].key, mstr->name, mstr->value);
|
||||
}
|
||||
//apr_table_setn(orig_col, orig_te[m].key, (void *)mstr );
|
||||
collection_original_setvar(g_msr, te[n].key, mstr);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
apr_table_clear(g_msr->collections_dirty);
|
||||
apr_table_clear(g_msr->collections_original);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Unknown type: \"%s\"\n", type);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ec != 0) {
|
||||
fprintf(stdout, "%s\n", errmsg ? errmsg : "");
|
||||
return ec;
|
||||
}
|
||||
}
|
||||
|
||||
if (iterations > 1) {
|
||||
double dT;
|
||||
T1 = apr_time_now();
|
||||
|
||||
dT = apr_time_as_msec(T1 - T0);
|
||||
|
||||
#ifdef VERBOSE
|
||||
if (i >= 100) {
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
fprintf(stdout, "%d @ %.4f msec per iteration.\n", iterations, dT / iterations);
|
||||
}
|
||||
fprintf(stdout, "%s\n", errmsg ? errmsg : "");
|
||||
|
||||
return ec;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#ifndef _UTIL_H_
|
||||
#define _UTIL_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <apr_file_info.h>
|
||||
|
||||
#ifndef APR_WSTICKY
|
||||
/* Add extra flags added to APR in 0.9.5 */
|
||||
#define APR_USETID 0x8000 /**< Set user id */
|
||||
#define APR_GSETID 0x4000 /**< Set group id */
|
||||
#define APR_WSTICKY 0x2000 /**< Sticky bit */
|
||||
#endif
|
||||
|
||||
#include "modsecurity.h"
|
||||
|
||||
int DSOLOCAL normalise_path_inplace(unsigned char *input, int len, int win, int *changed);
|
||||
|
||||
int DSOLOCAL parse_boolean(const char *input);
|
||||
|
||||
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 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);
|
||||
|
||||
unsigned char DSOLOCAL x2c(unsigned char *what);
|
||||
|
||||
unsigned char DSOLOCAL xsingle2c(unsigned char *what);
|
||||
|
||||
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 *template, int mode);
|
||||
|
||||
int DSOLOCAL msc_mkstemp(char *template);
|
||||
|
||||
char DSOLOCAL *strtolower_inplace(unsigned char *str);
|
||||
|
||||
unsigned char DSOLOCAL *c2x(unsigned what, unsigned char *where);
|
||||
|
||||
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_header_name(apr_pool_t *p, const char *text);
|
||||
|
||||
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);
|
||||
|
||||
char DSOLOCAL *_log_escape(apr_pool_t *p, const unsigned char *input,
|
||||
unsigned long int input_length, int escape_quotes, int escape_colon);
|
||||
|
||||
int DSOLOCAL decode_base64_ext(char *plain_text, const char *input, int input_len);
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#include "msc_xml.h"
|
||||
|
||||
|
||||
/**
|
||||
* Initialise XML parser.
|
||||
*/
|
||||
int xml_init(modsec_rec *msr, char **error_msg) {
|
||||
if (error_msg == NULL) return -1;
|
||||
*error_msg = NULL;
|
||||
|
||||
msr->xml = apr_pcalloc(msr->mp, sizeof(xml_data));
|
||||
if (msr->xml == NULL) return -1;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#ifndef _MSC_XML_H_
|
||||
#define _MSC_XML_H_
|
||||
|
||||
typedef struct xml_data xml_data;
|
||||
|
||||
#include "modsecurity.h"
|
||||
#include <libxml/xmlschemas.h>
|
||||
#include <libxml/xpath.h>
|
||||
|
||||
/* 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
|
||||
@@ -1,498 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#include "modsecurity.h"
|
||||
#include "apache2.h"
|
||||
#include "pdf_protect.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include "apr_sha1.h"
|
||||
|
||||
#define DEFAULT_TIMEOUT 10
|
||||
#define DEFAULT_TOKEN_NAME "PDFPTOKEN"
|
||||
#define ATTACHMENT_MIME_TYPE "application/octet-stream"
|
||||
#define NOTE_TWEAK_HEADERS "pdfp-note-tweakheaders"
|
||||
#define NOTE_DONE "pdfp-note-done"
|
||||
#define REDIRECT_STATUS HTTP_TEMPORARY_REDIRECT
|
||||
#define DISPOSITION_VALUE "attachment;"
|
||||
|
||||
// TODO We need ID and REV values for the PDF XSS alert.
|
||||
|
||||
// TODO It would be nice if the user could choose the ID/REV/SEVERITY/MESSAGE, etc.
|
||||
|
||||
static char *encode_sha1_base64(apr_pool_t *mp, const char *data) {
|
||||
unsigned char digest[APR_SHA1_DIGESTSIZE];
|
||||
apr_sha1_ctx_t context;
|
||||
|
||||
/* Calculate the hash first. */
|
||||
apr_sha1_init(&context);
|
||||
apr_sha1_update(&context, data, strlen(data));
|
||||
apr_sha1_final(digest, &context);
|
||||
|
||||
/* Now transform with transport-friendly hex encoding. */
|
||||
return bytes2hex(mp, digest, APR_SHA1_DIGESTSIZE);
|
||||
}
|
||||
|
||||
static char *create_hash(modsec_rec *msr,
|
||||
const char *time_string)
|
||||
{
|
||||
const char *content = NULL;
|
||||
|
||||
if (msr->txcfg->pdfp_secret == NULL) {
|
||||
msr_log(msr, 1, "PdfProtect: Unable to generate hash. Please configure SecPdfProtectSecret.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Our protection token is made out of the client's IP
|
||||
* address, the secret key, and the token expiry time.
|
||||
*/
|
||||
content = apr_pstrcat(msr->mp, msr->remote_addr, msr->txcfg->pdfp_secret,
|
||||
time_string, NULL);
|
||||
if (content == NULL) return NULL;
|
||||
|
||||
return encode_sha1_base64(msr->mp, content);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static char *create_token(modsec_rec *msr) {
|
||||
apr_time_t current_time;
|
||||
const char *time_string = NULL;
|
||||
const char *hash = NULL;
|
||||
int timeout = DEFAULT_TIMEOUT;
|
||||
|
||||
if (msr->txcfg->pdfp_timeout != -1) {
|
||||
timeout = msr->txcfg->pdfp_timeout;
|
||||
}
|
||||
|
||||
current_time = apr_time_sec(apr_time_now());
|
||||
time_string = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(current_time + timeout));
|
||||
if (time_string == NULL) return NULL;
|
||||
|
||||
hash = create_hash(msr, time_string);
|
||||
if (hash == NULL) return NULL;
|
||||
|
||||
return apr_pstrcat(msr->mp, hash, "|", time_string, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static char *construct_new_uri(modsec_rec *msr) {
|
||||
const char *token_parameter = NULL;
|
||||
const char *new_uri = NULL;
|
||||
const char *token = NULL;
|
||||
const char *token_name = DEFAULT_TOKEN_NAME;
|
||||
|
||||
token = create_token(msr);
|
||||
if (token == NULL) return NULL;
|
||||
|
||||
if (msr->txcfg->pdfp_token_name != NULL) {
|
||||
token_name = msr->txcfg->pdfp_token_name;
|
||||
}
|
||||
|
||||
token_parameter = apr_pstrcat(msr->mp, token_name, "=", token, NULL);
|
||||
if (token_parameter == NULL) return NULL;
|
||||
|
||||
if (msr->r->args == NULL) { /* No other parameters. */
|
||||
new_uri = apr_pstrcat(msr->mp, msr->r->uri, "?", token_parameter, "#PDFP", NULL);
|
||||
} else { /* Preserve existing paramters. */
|
||||
new_uri = apr_pstrcat(msr->mp, msr->r->uri, "?", msr->r->args, "&",
|
||||
token_parameter, "#PDFP", NULL);
|
||||
}
|
||||
|
||||
return (char *)new_uri;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static char *extract_token(modsec_rec *msr) {
|
||||
char *search_string = NULL;
|
||||
char *p = NULL, *t = NULL;
|
||||
const char *token_name = DEFAULT_TOKEN_NAME;
|
||||
|
||||
if ((msr->r == NULL)||(msr->r->args == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (msr->txcfg->pdfp_token_name != NULL) {
|
||||
token_name = msr->txcfg->pdfp_token_name;
|
||||
}
|
||||
|
||||
search_string = apr_pstrcat(msr->mp, msr->txcfg->pdfp_token_name, "=", NULL);
|
||||
if (search_string == NULL) return NULL;
|
||||
|
||||
p = strstr(msr->r->args, search_string);
|
||||
if (p == NULL) return NULL;
|
||||
|
||||
t = p = p + strlen(search_string);
|
||||
while ((*t != '\0')&&(*t != '&')) t++;
|
||||
|
||||
return apr_pstrmemdup(msr->mp, p, t - p);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int validate_time_string(const char *time_string) {
|
||||
char *p = (char *)time_string;
|
||||
|
||||
while(*p != '\0') {
|
||||
if (!isdigit(*p)) return 0;
|
||||
p++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int verify_token(modsec_rec *msr, const char *token, char **error_msg) {
|
||||
unsigned int current_time, expiry_time;
|
||||
const char *time_string = NULL;
|
||||
const char *given_hash = NULL;
|
||||
const char *hash = NULL;
|
||||
const char *p = NULL;
|
||||
|
||||
if (error_msg == NULL) return 0;
|
||||
*error_msg = NULL;
|
||||
|
||||
/* Split token into its parts - hash and expiry time. */
|
||||
p = strstr(token, "|");
|
||||
if (p == NULL) return 0;
|
||||
|
||||
given_hash = apr_pstrmemdup(msr->mp, token, p - token);
|
||||
time_string = p + 1;
|
||||
if (!validate_time_string(time_string)) {
|
||||
*error_msg = apr_psprintf(msr->mp, "PdfProtect: Invalid time string: %s",
|
||||
log_escape_nq(msr->mp, time_string));
|
||||
return 0;
|
||||
}
|
||||
expiry_time = atoi(time_string);
|
||||
|
||||
/* Check the hash. */
|
||||
hash = create_hash(msr, time_string);
|
||||
if (strcmp(given_hash, hash) != 0) {
|
||||
*error_msg = apr_psprintf(msr->mp, "PdfProtect: Invalid hash: %s (expected %s)",
|
||||
log_escape_nq(msr->mp, given_hash), log_escape_nq(msr->mp, hash));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check time. */
|
||||
current_time = apr_time_sec(apr_time_now());
|
||||
if (current_time > expiry_time) {
|
||||
*error_msg = apr_psprintf(msr->mp, "PdfProtect: Token has expired.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) {
|
||||
modsec_rec *msr = (modsec_rec *)f->ctx;
|
||||
|
||||
if (msr == NULL) {
|
||||
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server,
|
||||
"ModSecurity: Internal Error: Unable to retrieve context in PDF output filter.");
|
||||
|
||||
ap_remove_output_filter(f);
|
||||
|
||||
return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
if (msr->txcfg->pdfp_enabled == 1) {
|
||||
// TODO Should we look at err_headers_out too?
|
||||
const char *h_content_type = apr_table_get(f->r->headers_out, "Content-Type");
|
||||
|
||||
if (msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(msr, 9, "PdfProtect: r->content_type=%s, header C-T=%s",
|
||||
log_escape_nq(msr->mp, f->r->content_type),
|
||||
log_escape_nq(msr->mp, h_content_type));
|
||||
}
|
||||
|
||||
/* Have we been asked to tweak the headers? */
|
||||
if (apr_table_get(f->r->notes, NOTE_TWEAK_HEADERS) != NULL) {
|
||||
/* Yes! */
|
||||
apr_table_set(f->r->headers_out, "Content-Disposition", DISPOSITION_VALUE);
|
||||
f->r->content_type = ATTACHMENT_MIME_TYPE;
|
||||
}
|
||||
|
||||
/* Check if we've already done the necessary work in
|
||||
* the first phase.
|
||||
*/
|
||||
if (apr_table_get(f->r->notes, NOTE_DONE) != NULL) {
|
||||
/* We have, so return straight away. */
|
||||
ap_remove_output_filter(f);
|
||||
return ap_pass_brigade(f->next, bb_in);
|
||||
}
|
||||
|
||||
/* Proceed to detect dynamically-generated PDF files. */
|
||||
|
||||
// TODO application/x-pdf, application/vnd.fdf, application/vnd.adobe.xfdf,
|
||||
// application/vnd.adobe.xdp+xml, application/vnd.adobe.xfd+xml, application/vnd.pdf
|
||||
// application/acrobat, text/pdf, text/x-pdf ???
|
||||
if (((f->r->content_type != NULL)&&(strcasecmp(f->r->content_type, "application/pdf") == 0))
|
||||
|| ((h_content_type != NULL)&&(strcasecmp(h_content_type, "application/pdf") == 0)))
|
||||
{
|
||||
request_rec *r = f->r;
|
||||
const char *token = NULL;
|
||||
|
||||
if (msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(msr, 9, "PdfProtect: Detected a dynamically-generated PDF in %s",
|
||||
log_escape_nq(msr->mp, r->uri));
|
||||
}
|
||||
|
||||
/* If we are configured with ForcedDownload protection method then we
|
||||
* can do our thing here and finish early.
|
||||
*/
|
||||
if (msr->txcfg->pdfp_method == PDF_PROTECT_METHOD_FORCED_DOWNLOAD) {
|
||||
if (msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(msr, 9, "PdfProtect: Forcing download of a dynamically "
|
||||
"generated PDF file.");
|
||||
}
|
||||
|
||||
apr_table_set(f->r->headers_out, "Content-Disposition", DISPOSITION_VALUE);
|
||||
f->r->content_type = ATTACHMENT_MIME_TYPE;
|
||||
|
||||
ap_remove_output_filter(f);
|
||||
|
||||
return ap_pass_brigade(f->next, bb_in);
|
||||
}
|
||||
|
||||
/* If we are here that means TokenRedirection is the desired protection method. */
|
||||
|
||||
/* Is this a non-GET request? */
|
||||
if ((f->r->method_number != M_GET)&&
|
||||
((msr->txcfg->pdfp_only_get == 1)||(msr->txcfg->pdfp_only_get == -1))
|
||||
) {
|
||||
/* This is a non-GET request and we have been configured
|
||||
* not to intercept it. So we are going to tweak the headers
|
||||
* to force download.
|
||||
*/
|
||||
if (msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(msr, 9, "PdfProtect: Forcing download of a dynamically "
|
||||
"generated PDF file and non-GET method.");
|
||||
}
|
||||
|
||||
apr_table_set(f->r->headers_out, "Content-Disposition", DISPOSITION_VALUE);
|
||||
f->r->content_type = ATTACHMENT_MIME_TYPE;
|
||||
|
||||
ap_remove_output_filter(f);
|
||||
|
||||
return ap_pass_brigade(f->next, bb_in);
|
||||
}
|
||||
|
||||
/* Locate the protection token. */
|
||||
token = extract_token(msr);
|
||||
|
||||
if (token == NULL) { /* No token. */
|
||||
char *new_uri = NULL;
|
||||
|
||||
/* Create a new URI with the protection token inside. */
|
||||
new_uri = construct_new_uri(msr);
|
||||
if (new_uri != NULL) {
|
||||
/* Redirect user to the new URI. */
|
||||
if (msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(msr, 9, "PdfProtect: PDF request without a token - "
|
||||
"redirecting to %s.", log_escape_nq(msr->mp, new_uri));
|
||||
}
|
||||
|
||||
apr_table_set(r->headers_out, "Location", new_uri);
|
||||
|
||||
ap_remove_output_filter(f);
|
||||
|
||||
return send_error_bucket(msr, f, REDIRECT_STATUS);
|
||||
}
|
||||
} else { /* Token found. */
|
||||
char *my_error_msg = NULL;
|
||||
|
||||
/* Verify the token is valid. */
|
||||
|
||||
if (verify_token(msr, token, &my_error_msg)) { /* Valid. */
|
||||
/* Do nothing - serve the PDF file. */
|
||||
if (msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(msr, 9, "PdfProtect: PDF request with a valid token "
|
||||
"- serving PDF file normally.");
|
||||
}
|
||||
|
||||
/* Fall through. */
|
||||
} else { /* Not valid. */
|
||||
/* The token is not valid. We will tweak the response
|
||||
* to prevent the PDF file from opening in the browser.
|
||||
*/
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 9, "PdfProtect: PDF request with an invalid token "
|
||||
"- serving file as attachment.");
|
||||
}
|
||||
|
||||
apr_table_set(r->headers_out, "Content-Disposition", DISPOSITION_VALUE);
|
||||
r->content_type = ATTACHMENT_MIME_TYPE;
|
||||
|
||||
/* Fall through. */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ap_remove_output_filter(f);
|
||||
|
||||
return ap_pass_brigade(f->next, bb_in);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
int pdfp_check(modsec_rec *msr) {
|
||||
const char *token = NULL;
|
||||
char *uri = NULL;
|
||||
char *p = NULL;
|
||||
|
||||
if (msr->txcfg->pdfp_enabled != 1) {
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "PdfProtect: Not enabled here.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (msr->txcfg->pdfp_method != PDF_PROTECT_METHOD_TOKEN_REDIRECTION) {
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "PdfProtect: Configured with ForcedDownload as protection method, "
|
||||
"skipping detection on the inbound.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Then determine whether we need to act at
|
||||
* all. If the request is not for a PDF file
|
||||
* return straight away.
|
||||
*/
|
||||
|
||||
if (msr->r->uri == NULL) {
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "PdfProtect: Unable to inspect URI because it is NULL.");
|
||||
}
|
||||
|
||||
return -1; /* Error. */
|
||||
}
|
||||
|
||||
if (msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(msr, 9, "PdfProtect: URI=%s, filename=%s, QUERY_STRING=%s.",
|
||||
log_escape_nq(msr->mp, msr->r->uri), log_escape_nq(msr->mp, msr->r->filename),
|
||||
log_escape_nq(msr->mp, msr->r->args));
|
||||
}
|
||||
|
||||
uri = apr_pstrdup(msr->mp, msr->r->uri);
|
||||
if (uri == NULL) return -1; /* Error. */
|
||||
ap_str_tolower(uri);
|
||||
|
||||
/* Attempt to figure out if this is a request for a PDF file. We are
|
||||
* going to be liberal and look for the extension anywhere in the URI,
|
||||
* not just at the end.
|
||||
*/
|
||||
p = strstr(uri, ".pdf");
|
||||
if (p == NULL) {
|
||||
/* We do not think this is a PDF file. */
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "PdfProtect: No indication in the URI this is a "
|
||||
"request for a PDF file.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ignore request methods other than GET and HEAD if
|
||||
* configured to do so.
|
||||
*
|
||||
* TODO: Code here is only GET, not GET|HEAD as comment states
|
||||
*/
|
||||
if ((msr->r->method_number != M_GET)&&(msr->txcfg->pdfp_only_get != 0)) {
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "PdfProtect: Not intercepting request "
|
||||
"(method=%s/%d).", log_escape_nq(msr->mp, msr->r->method), msr->r->method_number);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We make a note for ourselves that we've already handled
|
||||
* the request.
|
||||
*/
|
||||
apr_table_set(msr->r->notes, NOTE_DONE, "1");
|
||||
|
||||
/* Locate the protection token. */
|
||||
token = extract_token(msr);
|
||||
|
||||
if (token == NULL) { /* No token. */
|
||||
char *new_uri = NULL;
|
||||
|
||||
/* Create a new URI with the protection token inside. */
|
||||
new_uri = construct_new_uri(msr);
|
||||
if (new_uri == NULL) return DECLINED;
|
||||
|
||||
/* Redirect user to the new URI. */
|
||||
if (msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(msr, 9, "PdfProtect: PDF request without a token - redirecting to %s.",
|
||||
log_escape_nq(msr->mp, new_uri));
|
||||
}
|
||||
|
||||
apr_table_set(msr->r->headers_out, "Location", new_uri);
|
||||
|
||||
return REDIRECT_STATUS;
|
||||
} else { /* Token found. */
|
||||
char *my_error_msg = NULL;
|
||||
|
||||
/* Verify the token is valid. */
|
||||
if (verify_token(msr, token, &my_error_msg)) { /* Valid. */
|
||||
/* Do nothing - serve the PDF file. */
|
||||
if (msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(msr, 9, "PdfProtect: PDF request with a valid token - "
|
||||
"serving PDF file normally.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else { /* Not valid. */
|
||||
/* The token is not valid. We will tweak the response
|
||||
* to prevent the PDF file from opening in the browser.
|
||||
*/
|
||||
|
||||
// TODO Log alert
|
||||
|
||||
/* Changing response headers before response generation phase takes
|
||||
* place is not really reliable. Although we do this we also make
|
||||
* a note for ourselves (in the output filter) to check the headers
|
||||
* again just before they are sent back to the end user.
|
||||
*/
|
||||
apr_table_set(msr->r->headers_out, "Content-Disposition", DISPOSITION_VALUE);
|
||||
msr->r->content_type = ATTACHMENT_MIME_TYPE;
|
||||
apr_table_set(msr->r->notes, NOTE_TWEAK_HEADERS, "1");
|
||||
|
||||
/* Proceed with response (PDF) generation. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#ifndef _PDF_PROTECT_H_
|
||||
#define _PDF_PROTECT_H_
|
||||
|
||||
#define PDF_PROTECT_METHOD_TOKEN_REDIRECTION 1
|
||||
#define PDF_PROTECT_METHOD_FORCED_DOWNLOAD 2
|
||||
|
||||
apr_status_t DSOLOCAL pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in);
|
||||
|
||||
int DSOLOCAL pdfp_check(modsec_rec *msr);
|
||||
|
||||
#endif
|
||||
@@ -1,671 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#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, "BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset));
|
||||
}
|
||||
msr_log(msr, 4, "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, "BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset));
|
||||
}
|
||||
msr_log(msr, 4, "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, "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, "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);
|
||||
|
||||
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, "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, "Removing key \"%s\" from collection.", key_to_expire + 9);
|
||||
msr_log(msr, 9, "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, "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, "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, "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));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
||||
if (existing_dbm == NULL) {
|
||||
apr_sdbm_close(dbm);
|
||||
dbm = NULL;
|
||||
}
|
||||
|
||||
if (expired && (msr->txcfg->debuglog_level >= 9)) {
|
||||
msr_log(msr, 9, "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, "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, "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, "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) {
|
||||
return collection_retrieve_ex(NULL, msr, col_name, col_key, col_key_len);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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, "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);
|
||||
|
||||
/* 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, "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, "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, "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, "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) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
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, "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, "Failed to write to DBM file \"%s\": %s", dbm_filename,
|
||||
get_apr_error(msr->mp, rc));
|
||||
goto error;
|
||||
}
|
||||
|
||||
apr_sdbm_close(dbm);
|
||||
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "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:
|
||||
|
||||
if (dbm) {
|
||||
apr_sdbm_close(dbm);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, 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, "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, "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, "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, "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, "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, "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, "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));
|
||||
goto error;
|
||||
}
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "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;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,389 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#ifndef _MSC_RE_H_
|
||||
#define _MSC_RE_H_
|
||||
|
||||
#define ABSOLUTE_VALUE 0
|
||||
#define POSITIVE_VALUE 1
|
||||
#define NEGATIVE_VALUE 2
|
||||
|
||||
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_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"
|
||||
|
||||
#if defined(WITH_LUA)
|
||||
#include "msc_lua.h"
|
||||
#endif
|
||||
|
||||
/* Actions, variables, functions and operator functions */
|
||||
|
||||
apr_status_t DSOLOCAL msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, char *var_name, char *var_value);
|
||||
|
||||
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);
|
||||
|
||||
apr_status_t DSOLOCAL msre_parse_targets(msre_ruleset *ruleset, const char *text,
|
||||
apr_array_header_t *arr, char **error_msg);
|
||||
|
||||
apr_status_t DSOLOCAL msre_parse_actions(msre_engine *engine, msre_actionset *actionset,
|
||||
const char *text, char **error_msg);
|
||||
|
||||
msre_var_metadata DSOLOCAL *msre_resolve_var(msre_engine *engine, const char *name);
|
||||
|
||||
msre_action_metadata DSOLOCAL *msre_resolve_action(msre_engine *engine, const char *name);
|
||||
|
||||
msre_var DSOLOCAL *msre_create_var(msre_ruleset *ruleset, const char *name, const char *param,
|
||||
modsec_rec *msr, char **error_msg);
|
||||
|
||||
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);
|
||||
|
||||
msre_action DSOLOCAL *msre_create_action(msre_engine *engine, const char *name,
|
||||
const char *param, 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);
|
||||
|
||||
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);
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
||||
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 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_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
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
apr_status_t DSOLOCAL msre_rule_process(msre_rule *rule, modsec_rec *msr);
|
||||
|
||||
#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 {
|
||||
const char *name;
|
||||
const char *value;
|
||||
unsigned int value_len;
|
||||
const 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;
|
||||
int severity;
|
||||
int phase;
|
||||
msre_rule *rule;
|
||||
|
||||
/* Flow */
|
||||
int is_chained;
|
||||
int skip_count;
|
||||
const char *skip_after;
|
||||
|
||||
/* Disruptive */
|
||||
int intercept_action;
|
||||
const char *intercept_uri;
|
||||
int intercept_status;
|
||||
int 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;
|
||||
};
|
||||
|
||||
char DSOLOCAL *msre_actionset_generate_action_string(apr_pool_t *pool, const msre_actionset *actionset);
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
/* -- 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
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,805 +0,0 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2010 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* This product is released under the terms of the General Public Licence,
|
||||
* version 2 (GPLv2). Please refer to the file LICENSE (included with this
|
||||
* distribution) which contains the complete text of the licence.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL
|
||||
* as it is applied to this software. View the full text of the exception in
|
||||
* file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
|
||||
* distribution.
|
||||
*
|
||||
* 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 support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
#include <ctype.h>
|
||||
|
||||
#include "apr_md5.h"
|
||||
#include "apr_sha1.h"
|
||||
#include "apr_base64.h"
|
||||
|
||||
#include "re.h"
|
||||
#include "msc_util.h"
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* normalisePath */
|
||||
|
||||
static int msre_fn_normalisePath_execute(apr_pool_t *mptmp, unsigned char *input,
|
||||
long int input_len, char **rval, long int *rval_len)
|
||||
{
|
||||
int changed;
|
||||
|
||||
*rval_len = normalise_path_inplace(input, input_len, 0, &changed);
|
||||
*rval = (char *)input;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* normalisePathWin */
|
||||
|
||||
static int msre_fn_normalisePathWin_execute(apr_pool_t *mptmp, unsigned char *input,
|
||||
long int input_len, char **rval, long int *rval_len)
|
||||
{
|
||||
int changed;
|
||||
|
||||
*rval_len = normalise_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 poil
|
||||
* \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_decodeBase64Ext_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 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
|
||||
);
|
||||
|
||||
/* 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_normalisePath_execute
|
||||
);
|
||||
|
||||
/* normalisePathWin */
|
||||
msre_engine_tfn_register(engine,
|
||||
"normalisePathWin",
|
||||
msre_fn_normalisePathWin_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
|
||||
);
|
||||
|
||||
/* 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
|
||||
);
|
||||
|
||||
/* 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
|
||||
);
|
||||
|
||||
/* urlEncode */
|
||||
msre_engine_tfn_register(engine,
|
||||
"urlEncode",
|
||||
msre_fn_urlEncode_execute
|
||||
);
|
||||
|
||||
/* decodeBase64Ext */
|
||||
msre_engine_tfn_register(engine,
|
||||
"decodeBase64Ext",
|
||||
msre_fn_decodeBase64Ext_execute
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +0,0 @@
|
||||
#!@PERL@
|
||||
#
|
||||
# Example to generate CSV performance data from test results taken from
|
||||
# test generated by gen_rx-pm.pl.
|
||||
#
|
||||
use strict;
|
||||
|
||||
my %H = ();
|
||||
while (<>) {
|
||||
chomp;
|
||||
my ($op, $label, $n, $i, $value) = (m/\s*\d+\)\s+\S+\s+"([^"]*)"\s+(\S+)\s+(\d+) item\(s\): passed\s+\((\d+)\s+\@\s+([-\+\d\.E]+) msec\s.*/);
|
||||
|
||||
next unless defined($value);
|
||||
$H{$n}{$label} = $value;
|
||||
|
||||
}
|
||||
|
||||
printf "%s, %s, %s, %s\n", qw(N rx1 rx2 pm1);
|
||||
for (sort {$a <=> $b} keys %H) {
|
||||
printf "%s, %s, %s, %s\n", $_, $H{$_}{rx1}, $H{$_}{rx2}, $H{$_}{pm1}
|
||||
};
|
||||
@@ -1,99 +0,0 @@
|
||||
#!@PERL@
|
||||
#
|
||||
# Generates a test file for comparing @rx and @pm speed.
|
||||
#
|
||||
use strict;
|
||||
use Regexp::Assemble;
|
||||
|
||||
srand(424242); # We want this static, so we can compare different runs
|
||||
|
||||
my $MIN = $ARGV[0] || 0;
|
||||
my $MAX = $ARGV[1] || 5000;
|
||||
my $INC = $ARGV[2] || int($MAX * .05);
|
||||
my $ITERATIONS = 10000;
|
||||
my $MINSTRLEN = 2;
|
||||
my $MAXSTRLEN = 8;
|
||||
|
||||
my $match = join '', ('a' .. 'z');
|
||||
my @param = ();
|
||||
my $i=$MIN;
|
||||
while ($i <= $MAX) {
|
||||
my $ra = Regexp::Assemble->new;
|
||||
|
||||
while (@param < $i) {
|
||||
unshift @param, rndstr();
|
||||
}
|
||||
|
||||
$ra->add(@param);
|
||||
|
||||
printf (
|
||||
"# rx: %6d\n".
|
||||
"{\n".
|
||||
" comment => \"rx1 %6d item(s)\",\n".
|
||||
" type => \"op\",\n".
|
||||
" name => \"rx\",\n".
|
||||
" param => qr/%s/,\n".
|
||||
" input => \"%s\",\n".
|
||||
" ret => " . (@param ? 0 : 1) . ",".
|
||||
" iterations => %d,\n".
|
||||
"},\n",
|
||||
$i,
|
||||
$i,
|
||||
(@param ? '(?:' . join('|', @param) . ')' : ""),
|
||||
$match,
|
||||
$ITERATIONS,
|
||||
);
|
||||
|
||||
printf (
|
||||
"# rx-optimized: %6d\n".
|
||||
"{\n".
|
||||
" comment => \"rx2 %6d item(s)\",\n".
|
||||
" type => \"op\",\n".
|
||||
" name => \"rx\",\n".
|
||||
" param => qr/%s/,\n".
|
||||
" input => \"%s\",\n".
|
||||
" ret => " . (@param ? 0 : 1) . ",".
|
||||
" iterations => %d,\n".
|
||||
"},\n",
|
||||
$i,
|
||||
$i,
|
||||
(@param ? $ra->as_string : ""),
|
||||
$match,
|
||||
$ITERATIONS,
|
||||
);
|
||||
|
||||
printf (
|
||||
"# pm: %6d\n".
|
||||
"{\n".
|
||||
" comment => \"pm1 %6d item(s)\",\n".
|
||||
" type => \"op\",\n".
|
||||
" name => \"pm\",\n".
|
||||
" param => \"%s\",\n".
|
||||
" input => \"%s\",\n".
|
||||
" ret => 0,".
|
||||
" iterations => %d,\n".
|
||||
"},\n",
|
||||
$i,
|
||||
$i,
|
||||
join(' ', @param ? @param : ("''")),
|
||||
$match,
|
||||
$ITERATIONS,
|
||||
);
|
||||
|
||||
$i = ($i == $MIN) ? ($i + $INC) - ($i % $INC) : $i + $INC;
|
||||
|
||||
}
|
||||
|
||||
sub rndstr {
|
||||
my @c = ('a' .. 'z');
|
||||
my $rndstr;
|
||||
my $max = int(rand($MAXSTRLEN - $MINSTRLEN)) + $MINSTRLEN;
|
||||
foreach (1 .. $max) {
|
||||
$rndstr .= $c[rand @c];
|
||||
}
|
||||
# We need a string that is not in another string for "last"
|
||||
if ($match =~ m/$rndstr/) {
|
||||
$rndstr = rndstr();
|
||||
}
|
||||
return $rndstr;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "beginsWith",
|
||||
param => "",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "beginsWith",
|
||||
param => "TestCase",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "beginsWith",
|
||||
param => "",
|
||||
input => "TestCase",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "beginsWith",
|
||||
param => "abcdef",
|
||||
input => "abcdef",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "beginsWith",
|
||||
param => "abcdef",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "beginsWith",
|
||||
param => "abcdef",
|
||||
input => "abc",
|
||||
ret => 0,
|
||||
},
|
||||
@@ -1,73 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "contains",
|
||||
param => "",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "contains",
|
||||
param => "TestCase",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "contains",
|
||||
param => "",
|
||||
input => "TestCase",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "contains",
|
||||
param => "abc",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "contains",
|
||||
param => "def",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "contains",
|
||||
param => "ghi",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "contains",
|
||||
param => "ghij",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "contains",
|
||||
param => "x",
|
||||
input => "x",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "contains",
|
||||
param => "y",
|
||||
input => "xyz",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "contains",
|
||||
param => "hiding",
|
||||
input => "hidinX<-not quite, but is later on->hiding",
|
||||
ret => 1,
|
||||
},
|
||||
@@ -1,108 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "TestCase",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "",
|
||||
input => "TestCase",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "abc",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "def",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "ghi",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "abc",
|
||||
input => "abc def ghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "def",
|
||||
input => "abc def ghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "ghi",
|
||||
input => "abc def ghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "abc",
|
||||
input => "abc\0def ghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "def",
|
||||
input => "abc\0def ghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "x",
|
||||
input => "x",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "x",
|
||||
input => " x ",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "y",
|
||||
input => "xyz",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "containsWord",
|
||||
param => "hiding",
|
||||
input => "hidingX<-not on word boundary, but is later on->hiding",
|
||||
ret => 1,
|
||||
},
|
||||
@@ -1,52 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "endsWith",
|
||||
param => "",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "endsWith",
|
||||
param => "TestCase",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "endsWith",
|
||||
param => "",
|
||||
input => "TestCase",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "endsWith",
|
||||
param => "abc",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "endsWith",
|
||||
param => "def",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "endsWith",
|
||||
param => "ghi",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "endsWith",
|
||||
param => "ghi",
|
||||
input => "abcdef\0ghi",
|
||||
ret => 1,
|
||||
},
|
||||
@@ -1,101 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "0",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "5",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### Invalid
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "xxx",
|
||||
input => "0",
|
||||
ret => 1,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "xxx",
|
||||
input => "5",
|
||||
ret => 0,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "xxx",
|
||||
input => "-1",
|
||||
ret => 0,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "0",
|
||||
input => "xxx",
|
||||
ret => 1,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "5",
|
||||
input => "xxx",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "0",
|
||||
input => "-5",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "0",
|
||||
input => "0",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "0",
|
||||
input => "5",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "5",
|
||||
input => "0",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "5",
|
||||
input => "5",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "eq",
|
||||
param => "5",
|
||||
input => "10",
|
||||
ret => 0,
|
||||
},
|
||||
@@ -1,93 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "ge",
|
||||
param => "0",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "ge",
|
||||
param => "5",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### Invalid
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "ge",
|
||||
param => "xxx",
|
||||
input => "5",
|
||||
ret => 1,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "ge",
|
||||
param => "xxx",
|
||||
input => "-1",
|
||||
ret => 0,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "ge",
|
||||
param => "0",
|
||||
input => "xxx",
|
||||
ret => 1,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "ge",
|
||||
param => "5",
|
||||
input => "xxx",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "ge",
|
||||
param => "0",
|
||||
input => "-5",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "ge",
|
||||
param => "0",
|
||||
input => "0",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "ge",
|
||||
param => "0",
|
||||
input => "5",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "ge",
|
||||
param => "5",
|
||||
input => "0",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "ge",
|
||||
param => "5",
|
||||
input => "5",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "ge",
|
||||
param => "5",
|
||||
input => "10",
|
||||
ret => 1,
|
||||
},
|
||||
@@ -1,44 +0,0 @@
|
||||
### Empty
|
||||
# NOTE: All will return 0 because of lacking DB
|
||||
{
|
||||
type => "op",
|
||||
name => "geoLookup",
|
||||
param => "",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "geoLookup",
|
||||
param => "TestCase",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
# Failed Lookup
|
||||
{
|
||||
type => "op",
|
||||
name => "geoLookup",
|
||||
param => "",
|
||||
input => "127.0.0.1",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
# Good
|
||||
{
|
||||
type => "op",
|
||||
name => "geoLookup",
|
||||
param => "",
|
||||
input => "216.75.21.122",
|
||||
#ret => 1,
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "geoLookup",
|
||||
param => "",
|
||||
input => "www.modsecurity.org",
|
||||
#ret => 1,
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "gt",
|
||||
param => "0",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "gt",
|
||||
param => "5",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### Invalid
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "gt",
|
||||
param => "xxx",
|
||||
input => "5",
|
||||
ret => 1,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "gt",
|
||||
param => "xxx",
|
||||
input => "-1",
|
||||
ret => 0,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "gt",
|
||||
param => "-1",
|
||||
input => "xxx",
|
||||
ret => 1,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "gt",
|
||||
param => "5",
|
||||
input => "xxx",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "gt",
|
||||
param => "0",
|
||||
input => "-5",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "gt",
|
||||
param => "0",
|
||||
input => "0",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "gt",
|
||||
param => "0",
|
||||
input => "5",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "gt",
|
||||
param => "5",
|
||||
input => "0",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "gt",
|
||||
param => "5",
|
||||
input => "5",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "gt",
|
||||
param => "5",
|
||||
input => "10",
|
||||
ret => 1,
|
||||
},
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "le",
|
||||
param => "0",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "le",
|
||||
param => "5",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
### Invalid
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "le",
|
||||
param => "xxx",
|
||||
input => "5",
|
||||
ret => 0,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "le",
|
||||
param => "xxx",
|
||||
input => "-1",
|
||||
ret => 1,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "le",
|
||||
param => "0",
|
||||
input => "xxx",
|
||||
ret => 1,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "le",
|
||||
param => "5",
|
||||
input => "xxx",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "le",
|
||||
param => "0",
|
||||
input => "-5",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "le",
|
||||
param => "0",
|
||||
input => "0",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "le",
|
||||
param => "0",
|
||||
input => "5",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "le",
|
||||
param => "5",
|
||||
input => "0",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "le",
|
||||
param => "5",
|
||||
input => "5",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "le",
|
||||
param => "5",
|
||||
input => "10",
|
||||
ret => 0,
|
||||
},
|
||||
@@ -1,93 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "lt",
|
||||
param => "0",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "lt",
|
||||
param => "5",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
### Invalid
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "lt",
|
||||
param => "xxx",
|
||||
input => "5",
|
||||
ret => 0,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "lt",
|
||||
param => "xxx",
|
||||
input => "-1",
|
||||
ret => 1,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "lt",
|
||||
param => "-1",
|
||||
input => "xxx",
|
||||
ret => 0,
|
||||
},
|
||||
# xxx interpreted as 0
|
||||
{
|
||||
type => "op",
|
||||
name => "lt",
|
||||
param => "5",
|
||||
input => "xxx",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "lt",
|
||||
param => "0",
|
||||
input => "-5",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "lt",
|
||||
param => "0",
|
||||
input => "0",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "lt",
|
||||
param => "0",
|
||||
input => "5",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "lt",
|
||||
param => "5",
|
||||
input => "0",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "lt",
|
||||
param => "5",
|
||||
input => "5",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "lt",
|
||||
param => "5",
|
||||
input => "10",
|
||||
ret => 0,
|
||||
},
|
||||
@@ -1,52 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "m",
|
||||
param => "",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "m",
|
||||
param => "TestCase",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "m",
|
||||
param => "",
|
||||
input => "TestCase",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "m",
|
||||
param => "abc",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "m",
|
||||
param => "def",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "m",
|
||||
param => "ghi",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "m",
|
||||
param => "ghij",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
@@ -1,23 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "noMatch",
|
||||
param => "",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "noMatch",
|
||||
param => "TestCase",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "noMatch",
|
||||
param => "",
|
||||
input => "TestCase",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "TestCase",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "abc",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "def",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "ghi",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "ghij",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### Multiple
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "abc def ghi",
|
||||
input => "abcxxxyyy",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "abc def ghi",
|
||||
input => "xxxabcyyy",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "abc def ghi",
|
||||
input => "xxxyyyabc",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "abc def ghi",
|
||||
input => "defxxxyyy",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "abc def ghi",
|
||||
input => "xxxdefyyy",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "abc def ghi",
|
||||
input => "xxxyyydef",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "abc def ghi",
|
||||
input => "ghixxxyyy",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "abc def ghi",
|
||||
input => "xxxghiyyy",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "abc def ghi",
|
||||
input => "xxxyyyghi",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
### Long
|
||||
{
|
||||
type => "op",
|
||||
name => "pm",
|
||||
param => "000 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999",
|
||||
input => "xxxyyy999",
|
||||
ret => 1,
|
||||
},
|
||||
@@ -1,4 +0,0 @@
|
||||
abc
|
||||
def
|
||||
ghi
|
||||
xxx yyy zzz
|
||||
@@ -1,45 +0,0 @@
|
||||
### No Match
|
||||
{
|
||||
type => "op",
|
||||
name => "pmFromFile",
|
||||
param => "op/pmFromFile-01.dat",
|
||||
input => "xxxyyyzzz",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### Multiple
|
||||
{
|
||||
type => "op",
|
||||
name => "pmFromFile",
|
||||
param => "op/pmFromFile-01.dat",
|
||||
input => "defxxxyyy",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pmFromFile",
|
||||
param => "op/pmFromFile-01.dat",
|
||||
input => "xxxdefyyy",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pmFromFile",
|
||||
param => "op/pmFromFile-01.dat",
|
||||
input => "xxxyyydef",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pmFromFile",
|
||||
param => "op/pmFromFile-01.dat",
|
||||
input => "xxx yyy zzz",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "pmFromFile",
|
||||
param => "op/pmFromFile-01.dat",
|
||||
input => "xxx yyy",
|
||||
ret => 0,
|
||||
},
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "rx",
|
||||
param => "",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "rx",
|
||||
param => "TestCase",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "rx",
|
||||
param => "",
|
||||
input => "TestCase",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "rx",
|
||||
param => "abc",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "rx",
|
||||
param => "def",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "rx",
|
||||
param => "ghi",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "rx",
|
||||
param => "ghij",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### Complex regex
|
||||
{
|
||||
type => "op",
|
||||
name => "rx",
|
||||
param => qr/^([^=])\s*=\s*((?:abc)+(?:def|ghi){2})$/i,
|
||||
input => "x =AbCDeFgHi",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "streq",
|
||||
param => "",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "streq",
|
||||
param => "TestCase",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "streq",
|
||||
param => "",
|
||||
input => "TestCase",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "streq",
|
||||
param => "abc",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "streq",
|
||||
param => "def",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "streq",
|
||||
param => "ghi",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "streq",
|
||||
param => "abcdefghi",
|
||||
input => "abcdefghi",
|
||||
ret => 1,
|
||||
},
|
||||
@@ -1,23 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "unconditionalMatch",
|
||||
param => "",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "unconditionalMatch",
|
||||
param => "TestCase",
|
||||
input => "",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "unconditionalMatch",
|
||||
param => "",
|
||||
input => "TestCase",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "validateByteRange",
|
||||
param => "0-255",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateByteRange",
|
||||
param => "",
|
||||
input => "TestCase",
|
||||
ret => 1,
|
||||
},
|
||||
|
||||
### Invalid
|
||||
{
|
||||
type => "op",
|
||||
name => "validateByteRange",
|
||||
param => "xxx",
|
||||
input => "TestCase",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateByteRange",
|
||||
param => "xxx",
|
||||
input => "\x00",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "validateByteRange",
|
||||
param => "0-255",
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateByteRange",
|
||||
param => ord("a")."-".ord("i"),
|
||||
input => "abcdefghi",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateByteRange",
|
||||
param => ord("a")."-".ord("i"),
|
||||
input => "abcdefghij",
|
||||
ret => 1,
|
||||
},
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
### Empty
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "",
|
||||
ret => 0,
|
||||
},
|
||||
|
||||
### General
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "Hello%20World!",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "Hello+World!",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "HelloWorld!",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "%00Hello%20World!",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "Hello%20World!%00",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "%00",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "%ff",
|
||||
ret => 0,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "%0",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "%f",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "%",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "%0z",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "%z0",
|
||||
ret => 1,
|
||||
},
|
||||
{
|
||||
type => "op",
|
||||
name => "validateUrlEncoding",
|
||||
param => "",
|
||||
input => "%0%",
|
||||
ret => 1,
|
||||
},
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user