This commit is contained in:
brenosilva
2011-01-06 19:18:48 +00:00
parent e3f0a7f670
commit 47c791938f
196 changed files with 79551 additions and 0 deletions

791
2.5.13/2.5.x/CHANGES Normal file
View File

@@ -0,0 +1,791 @@
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.

281
2.5.13/2.5.x/LICENSE Normal file
View File

@@ -0,0 +1,281 @@
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

View File

@@ -0,0 +1,137 @@
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

17
2.5.13/2.5.x/README.TXT Normal file
View File

@@ -0,0 +1,17 @@
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.

View File

@@ -0,0 +1,181 @@
============================================================
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".

View File

View File

@@ -0,0 +1,127 @@
# 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

View File

@@ -0,0 +1,73 @@
###########################################################################
### 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

810
2.5.13/2.5.x/apache2/acmp.c Normal file
View File

@@ -0,0 +1,810 @@
/*
* 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;
}

125
2.5.13/2.5.x/apache2/acmp.h Normal file
View File

@@ -0,0 +1,125 @@
/*
* 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_*/

View File

@@ -0,0 +1,108 @@
/*
* 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

View File

@@ -0,0 +1,823 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,433 @@
/*
* 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;
}

View File

@@ -0,0 +1,76 @@
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"

View File

@@ -0,0 +1,171 @@
#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;
}

View File

@@ -0,0 +1,88 @@
#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 */
};

View File

@@ -0,0 +1,99 @@
#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 */
};

View File

@@ -0,0 +1,6 @@
#!/bin/sh
rm -rf autom4te.cache
#automake --add-missing --copy
autoreconf --install

View File

@@ -0,0 +1,15 @@
#!@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

View File

@@ -0,0 +1,82 @@
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
])

View File

@@ -0,0 +1,82 @@
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
])

View File

@@ -0,0 +1,100 @@
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
])

View File

@@ -0,0 +1,184 @@
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
])

View File

@@ -0,0 +1,81 @@
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
])

View File

@@ -0,0 +1,74 @@
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
])

View File

@@ -0,0 +1,224 @@
#!/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 Normal file

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 Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,441 @@
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

View File

@@ -0,0 +1,76 @@
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.

View File

@@ -0,0 +1,70 @@
# 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

View File

@@ -0,0 +1,57 @@
###########################################################################
### 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

View File

@@ -0,0 +1,151 @@
#!@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},
);
}

View File

@@ -0,0 +1,98 @@
##########################################################################
# 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

View File

@@ -0,0 +1,142 @@
/* 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

View File

@@ -0,0 +1 @@
/* This file is left empty for building on Windows. */

View File

@@ -0,0 +1,629 @@
/*
* 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;
}

View File

@@ -0,0 +1,572 @@
/*
* 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

View File

@@ -0,0 +1,19 @@
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

View File

@@ -0,0 +1,519 @@
/*
* 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;
}

View File

@@ -0,0 +1,72 @@
/*
* 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

View File

@@ -0,0 +1,983 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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

View File

@@ -0,0 +1,484 @@
/*
* 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 */

View File

@@ -0,0 +1,51 @@
/*
* 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

View File

@@ -0,0 +1,144 @@
/*
* 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

View File

@@ -0,0 +1,346 @@
/*
* 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);
}

View File

@@ -0,0 +1,33 @@
/*
* 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

View File

@@ -0,0 +1,189 @@
/*
* 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);
}

View File

@@ -0,0 +1,67 @@
/*
* 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

View File

@@ -0,0 +1,42 @@
/*
* 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 */
}

View File

@@ -0,0 +1,68 @@
/*
* 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_ */

View File

@@ -0,0 +1,760 @@
/*
* 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;
}

View File

@@ -0,0 +1,921 @@
/*
* 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, &param_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

View File

@@ -0,0 +1,115 @@
/*
* 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

View File

@@ -0,0 +1,139 @@
/*
* 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;
}

View File

@@ -0,0 +1,49 @@
/*
* 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

View File

@@ -0,0 +1,498 @@
/*
* 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;
}

View File

@@ -0,0 +1,29 @@
/*
* 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

View File

@@ -0,0 +1,671 @@
/*
* 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;
}

View File

@@ -0,0 +1,32 @@
/*
* 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

2509
2.5.13/2.5.x/apache2/re.c Normal file

File diff suppressed because it is too large Load Diff

389
2.5.13/2.5.x/apache2/re.h Normal file
View File

@@ -0,0 +1,389 @@
/*
* 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

View File

@@ -0,0 +1,805 @@
/*
* 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

View File

View File

@@ -0,0 +1,21 @@
#!@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}
};

View File

@@ -0,0 +1,99 @@
#!@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;
}

View File

@@ -0,0 +1,45 @@
### 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,
},

View File

@@ -0,0 +1,73 @@
### 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,
},

View File

@@ -0,0 +1,108 @@
### 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,
},

View File

@@ -0,0 +1,52 @@
### 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,
},

View File

@@ -0,0 +1,101 @@
### 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,
},

View File

@@ -0,0 +1,93 @@
### 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,
},

View File

@@ -0,0 +1,44 @@
### 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,
},

View File

@@ -0,0 +1,93 @@
### 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,
},

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,93 @@
### 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,
},

View File

@@ -0,0 +1,93 @@
### 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,
},

View File

@@ -0,0 +1,52 @@
### 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,
},

View File

@@ -0,0 +1,23 @@
### 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,
},

View File

@@ -0,0 +1,112 @@
### 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,
},

View File

@@ -0,0 +1,4 @@
abc
def
ghi
xxx yyy zzz

View File

@@ -0,0 +1,45 @@
### 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,
},

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,62 @@
### 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,
},

View File

@@ -0,0 +1,52 @@
### 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,
},

View File

@@ -0,0 +1,23 @@
### 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,
},

View File

@@ -0,0 +1,54 @@
### 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,
},

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,101 @@
### 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