From 47c791938faefd37d8ab3981e9cbe29e071ee9bd Mon Sep 17 00:00:00 2001 From: brenosilva Date: Thu, 6 Jan 2011 19:18:48 +0000 Subject: [PATCH] --- 2.5.13/2.5.x/CHANGES | 791 ++ 2.5.13/2.5.x/LICENSE | 281 + 2.5.13/2.5.x/MODSECURITY_LICENSING_EXCEPTION | 137 + 2.5.13/2.5.x/README.TXT | 17 + 2.5.13/2.5.x/README_WINDOWS.TXT | 181 + 2.5.13/2.5.x/apache2/.deps | 0 2.5.13/2.5.x/apache2/Makefile.in | 127 + 2.5.13/2.5.x/apache2/Makefile.win | 73 + 2.5.13/2.5.x/apache2/acmp.c | 810 ++ 2.5.13/2.5.x/apache2/acmp.h | 125 + 2.5.13/2.5.x/apache2/apache2.h | 108 + 2.5.13/2.5.x/apache2/apache2_config.c | 2432 +++++ 2.5.13/2.5.x/apache2/apache2_io.c | 823 ++ 2.5.13/2.5.x/apache2/apache2_util.c | 433 + 2.5.13/2.5.x/apache2/api/README | 76 + 2.5.13/2.5.x/apache2/api/mod_op_strstr.c | 171 + 2.5.13/2.5.x/apache2/api/mod_tfn_reverse.c | 88 + .../apache2/api/mod_var_remote_addr_port.c | 99 + 2.5.13/2.5.x/apache2/autogen.sh | 6 + 2.5.13/2.5.x/apache2/build/apxs-wrapper.in | 15 + 2.5.13/2.5.x/apache2/build/find_apr.m4 | 82 + 2.5.13/2.5.x/apache2/build/find_apu.m4 | 82 + 2.5.13/2.5.x/apache2/build/find_curl.m4 | 100 + 2.5.13/2.5.x/apache2/build/find_lua.m4 | 184 + 2.5.13/2.5.x/apache2/build/find_pcre.m4 | 81 + 2.5.13/2.5.x/apache2/build/find_xml.m4 | 74 + 2.5.13/2.5.x/apache2/build/install-sh | 224 + 2.5.13/2.5.x/apache2/build/libtool.m4 | 7373 +++++++++++++++ 2.5.13/2.5.x/apache2/build/ltmain.sh | 8412 +++++++++++++++++ 2.5.13/2.5.x/apache2/configure | 6564 +++++++++++++ 2.5.13/2.5.x/apache2/configure.in | 441 + 2.5.13/2.5.x/apache2/mlogc-src/INSTALL | 76 + 2.5.13/2.5.x/apache2/mlogc-src/Makefile.in | 70 + 2.5.13/2.5.x/apache2/mlogc-src/Makefile.win | 57 + .../apache2/mlogc-src/mlogc-batch-load.pl.in | 151 + .../apache2/mlogc-src/mlogc-default.conf | 98 + 2.5.13/2.5.x/apache2/mlogc-src/mlogc.c | 2311 +++++ 2.5.13/2.5.x/apache2/mod_security2.c | 1250 +++ .../2.5.x/apache2/mod_security2_config.h.in | 142 + 2.5.13/2.5.x/apache2/mod_security2_config.hw | 1 + 2.5.13/2.5.x/apache2/modsecurity.c | 629 ++ 2.5.13/2.5.x/apache2/modsecurity.h | 572 ++ 2.5.13/2.5.x/apache2/modules.mk | 19 + 2.5.13/2.5.x/apache2/msc_geo.c | 519 + 2.5.13/2.5.x/apache2/msc_geo.h | 72 + 2.5.13/2.5.x/apache2/msc_logging.c | 983 ++ 2.5.13/2.5.x/apache2/msc_logging.h | 54 + 2.5.13/2.5.x/apache2/msc_lua.c | 484 + 2.5.13/2.5.x/apache2/msc_lua.h | 51 + 2.5.13/2.5.x/apache2/msc_multipart.c | 1396 +++ 2.5.13/2.5.x/apache2/msc_multipart.h | 144 + 2.5.13/2.5.x/apache2/msc_parsers.c | 346 + 2.5.13/2.5.x/apache2/msc_parsers.h | 33 + 2.5.13/2.5.x/apache2/msc_pcre.c | 189 + 2.5.13/2.5.x/apache2/msc_pcre.h | 67 + 2.5.13/2.5.x/apache2/msc_release.c | 42 + 2.5.13/2.5.x/apache2/msc_release.h | 68 + 2.5.13/2.5.x/apache2/msc_reqbody.c | 760 ++ 2.5.13/2.5.x/apache2/msc_test.c | 921 ++ 2.5.13/2.5.x/apache2/msc_util.c | 1628 ++++ 2.5.13/2.5.x/apache2/msc_util.h | 115 + 2.5.13/2.5.x/apache2/msc_xml.c | 139 + 2.5.13/2.5.x/apache2/msc_xml.h | 49 + 2.5.13/2.5.x/apache2/pdf_protect.c | 498 + 2.5.13/2.5.x/apache2/pdf_protect.h | 29 + 2.5.13/2.5.x/apache2/persist_dbm.c | 671 ++ 2.5.13/2.5.x/apache2/persist_dbm.h | 32 + 2.5.13/2.5.x/apache2/re.c | 2509 +++++ 2.5.13/2.5.x/apache2/re.h | 389 + 2.5.13/2.5.x/apache2/re_actions.c | 2421 +++++ 2.5.13/2.5.x/apache2/re_operators.c | 2205 +++++ 2.5.13/2.5.x/apache2/re_tfns.c | 805 ++ 2.5.13/2.5.x/apache2/re_variables.c | 3188 +++++++ 2.5.13/2.5.x/apache2/t/action/.empty | 0 2.5.13/2.5.x/apache2/t/csv_rx-pm.pl.in | 21 + 2.5.13/2.5.x/apache2/t/gen_rx-pm.pl.in | 99 + 2.5.13/2.5.x/apache2/t/op/beginsWith.t | 45 + 2.5.13/2.5.x/apache2/t/op/contains.t | 73 + 2.5.13/2.5.x/apache2/t/op/containsWord.t | 108 + 2.5.13/2.5.x/apache2/t/op/endsWith.t | 52 + 2.5.13/2.5.x/apache2/t/op/eq.t | 101 + 2.5.13/2.5.x/apache2/t/op/ge.t | 93 + 2.5.13/2.5.x/apache2/t/op/geoLookup.t | 44 + 2.5.13/2.5.x/apache2/t/op/gt.t | 93 + 2.5.13/2.5.x/apache2/t/op/inspectFile.t | 1 + 2.5.13/2.5.x/apache2/t/op/le.t | 93 + 2.5.13/2.5.x/apache2/t/op/lt.t | 93 + 2.5.13/2.5.x/apache2/t/op/m.t | 52 + 2.5.13/2.5.x/apache2/t/op/noMatch.t | 23 + 2.5.13/2.5.x/apache2/t/op/pm.t | 112 + 2.5.13/2.5.x/apache2/t/op/pmFromFile-01.dat | 4 + 2.5.13/2.5.x/apache2/t/op/pmFromFile.t | 45 + 2.5.13/2.5.x/apache2/t/op/rbl.t | 1 + 2.5.13/2.5.x/apache2/t/op/rx.t | 62 + 2.5.13/2.5.x/apache2/t/op/streq.t | 52 + .../2.5.x/apache2/t/op/unconditionalMatch.t | 23 + 2.5.13/2.5.x/apache2/t/op/validateByteRange.t | 54 + 2.5.13/2.5.x/apache2/t/op/validateDTD.t | 1 + 2.5.13/2.5.x/apache2/t/op/validateSchema.t | 1 + .../2.5.x/apache2/t/op/validateUrlEncoding.t | 101 + .../2.5.x/apache2/t/op/validateUtf8Encoding.t | 260 + 2.5.13/2.5.x/apache2/t/op/verifyCC.t | 514 + 2.5.13/2.5.x/apache2/t/op/within.t | 52 + .../regression/action/00-disruptive-actions.t | 531 ++ .../apache2/t/regression/action/00-meta.t | 8 + .../apache2/t/regression/action/00-misc.t | 22 + .../t/regression/action/00-transformations.t | 8 + .../t/regression/action/10-append-prepend.t | 49 + .../apache2/t/regression/action/10-ctl.t | 56 + .../action/10-detectiononly-actions.t | 545 ++ .../apache2/t/regression/action/10-logging.t | 564 ++ .../t/regression/config/00-load-modsec.t | 23 + .../t/regression/config/10-audit-directives.t | 264 + .../t/regression/config/10-debug-directives.t | 278 + .../t/regression/config/10-misc-directives.t | 148 + .../regression/config/10-request-directives.t | 548 ++ .../config/10-response-directives.t | 174 + .../apache2/t/regression/config/20-chroot.t | 35 + .../t/regression/misc/00-multipart-parser.t | 1815 ++++ .../apache2/t/regression/misc/00-phases.t | 151 + .../2.5.x/apache2/t/regression/misc/10-pcre.t | 38 + .../apache2/t/regression/misc/10-tfn-cache.t | 189 + .../apache2/t/regression/misc/20-pdf-xss.t | 54 + .../apache2/t/regression/rule/00-basics.t | 91 + .../t/regression/rule/00-inheritance.t | 4 + .../apache2/t/regression/rule/00-script.t | 63 + .../2.5.x/apache2/t/regression/rule/10-xml.t | 419 + .../apache2/t/regression/rule/20-exceptions.t | 176 + .../server_root/conf/SoapEnvelope-bad.dtd | 8 + .../server_root/conf/SoapEnvelope-bad.xsd | 126 + .../server_root/conf/SoapEnvelope.dtd | 8 + .../server_root/conf/SoapEnvelope.xsd | 126 + .../regression/server_root/conf/httpd.conf.in | 37 + .../t/regression/server_root/conf/match.lua | 14 + .../t/regression/server_root/conf/test.lua | 14 + .../t/regression/server_root/data/.empty | 0 .../t/regression/server_root/htdocs/8k.txt | Bin 0 -> 8192 bytes .../regression/server_root/htdocs/index.html | 1 + .../t/regression/server_root/htdocs/test.pdf | Bin 0 -> 15406 bytes .../t/regression/server_root/htdocs/test.txt | 1 + .../t/regression/server_root/htdocs/test2.txt | 1 + .../regression/server_root/logs/audit/.empty | 0 .../regression/server_root/logs/subdir/.empty | 0 .../t/regression/server_root/tmp/.empty | 0 .../t/regression/server_root/upload/.empty | 0 .../apache2/t/regression/target/00-targets.t | 577 ++ .../apache2/t/run-regression-tests.pl.in | 779 ++ 2.5.13/2.5.x/apache2/t/run-unit-tests.pl.in | 161 + 2.5.13/2.5.x/apache2/t/tfn/base64Decode.t | 51 + 2.5.13/2.5.x/apache2/t/tfn/base64Encode.t | 40 + .../2.5.x/apache2/t/tfn/compressWhitespace.t | 50 + 2.5.13/2.5.x/apache2/t/tfn/cssDecode.t | 58 + 2.5.13/2.5.x/apache2/t/tfn/escapeSeqDecode.t | 114 + 2.5.13/2.5.x/apache2/t/tfn/hexDecode.t | 50 + 2.5.13/2.5.x/apache2/t/tfn/hexEncode.t | 26 + 2.5.13/2.5.x/apache2/t/tfn/htmlEntityDecode.t | 58 + 2.5.13/2.5.x/apache2/t/tfn/jsDecode.t | 129 + 2.5.13/2.5.x/apache2/t/tfn/length.t | 44 + 2.5.13/2.5.x/apache2/t/tfn/lowercase.t | 40 + 2.5.13/2.5.x/apache2/t/tfn/md5.t | 26 + 2.5.13/2.5.x/apache2/t/tfn/normalisePath.t | 224 + 2.5.13/2.5.x/apache2/t/tfn/normalisePathWin.t | 224 + 2.5.13/2.5.x/apache2/t/tfn/parityEven7bit.t | 34 + 2.5.13/2.5.x/apache2/t/tfn/parityOdd7bit.t | 34 + 2.5.13/2.5.x/apache2/t/tfn/parityZero7bit.t | 33 + 2.5.13/2.5.x/apache2/t/tfn/removeNulls.t | 62 + 2.5.13/2.5.x/apache2/t/tfn/removeWhitespace.t | 43 + 2.5.13/2.5.x/apache2/t/tfn/replaceComments.t | 155 + 2.5.13/2.5.x/apache2/t/tfn/replaceNulls.t | 55 + 2.5.13/2.5.x/apache2/t/tfn/sha1.t | 26 + 2.5.13/2.5.x/apache2/t/tfn/trim.t | 68 + 2.5.13/2.5.x/apache2/t/tfn/trimLeft.t | 69 + 2.5.13/2.5.x/apache2/t/tfn/trimRight.t | 68 + 2.5.13/2.5.x/apache2/t/tfn/urlDecode.t | 143 + 2.5.13/2.5.x/apache2/t/tfn/urlDecodeUni.t | 235 + 2.5.13/2.5.x/apache2/t/tfn/urlEncode.t | 129 + 2.5.13/2.5.x/apache2/utf8tables.h | 818 ++ 2.5.13/2.5.x/authors.txt | 7 + 2.5.13/2.5.x/doc/Makefile | 45 + .../doc/apache_request_cycle-modsecurity.jpg | Bin 0 -> 92271 bytes 2.5.13/2.5.x/doc/html-chunked.xsl | 293 + 2.5.13/2.5.x/doc/html.xsl | 21 + 2.5.13/2.5.x/doc/main-index.html | 35 + 2.5.13/2.5.x/doc/migration-matrix.html | 74 + 2.5.13/2.5.x/doc/migration-matrix.xml | 814 ++ 2.5.13/2.5.x/doc/modsecurity-logo.png | Bin 0 -> 100823 bytes 2.5.13/2.5.x/doc/modsecurity-reference.css | 102 + 2.5.13/2.5.x/doc/modsecurity.gif | Bin 0 -> 2585 bytes .../doc/modsecurity2-apache-reference.xml | 6378 +++++++++++++ .../2.5.x/doc/modsecurity2-data-formats.xml | 979 ++ 2.5.13/2.5.x/doc/pdf.xsl | 166 + 2.5.13/2.5.x/doc/trustwave-logo-small.gif | Bin 0 -> 4654 bytes 2.5.13/2.5.x/modsecurity.conf-minimal | 76 + 2.5.13/2.5.x/tools/README | 6 + 2.5.13/2.5.x/tools/rules-updater-example.conf | 23 + 2.5.13/2.5.x/tools/rules-updater.pl.in | 454 + 196 files changed, 79551 insertions(+) create mode 100644 2.5.13/2.5.x/CHANGES create mode 100644 2.5.13/2.5.x/LICENSE create mode 100644 2.5.13/2.5.x/MODSECURITY_LICENSING_EXCEPTION create mode 100644 2.5.13/2.5.x/README.TXT create mode 100644 2.5.13/2.5.x/README_WINDOWS.TXT create mode 100644 2.5.13/2.5.x/apache2/.deps create mode 100644 2.5.13/2.5.x/apache2/Makefile.in create mode 100644 2.5.13/2.5.x/apache2/Makefile.win create mode 100644 2.5.13/2.5.x/apache2/acmp.c create mode 100644 2.5.13/2.5.x/apache2/acmp.h create mode 100644 2.5.13/2.5.x/apache2/apache2.h create mode 100644 2.5.13/2.5.x/apache2/apache2_config.c create mode 100644 2.5.13/2.5.x/apache2/apache2_io.c create mode 100644 2.5.13/2.5.x/apache2/apache2_util.c create mode 100644 2.5.13/2.5.x/apache2/api/README create mode 100644 2.5.13/2.5.x/apache2/api/mod_op_strstr.c create mode 100644 2.5.13/2.5.x/apache2/api/mod_tfn_reverse.c create mode 100644 2.5.13/2.5.x/apache2/api/mod_var_remote_addr_port.c create mode 100755 2.5.13/2.5.x/apache2/autogen.sh create mode 100755 2.5.13/2.5.x/apache2/build/apxs-wrapper.in create mode 100644 2.5.13/2.5.x/apache2/build/find_apr.m4 create mode 100644 2.5.13/2.5.x/apache2/build/find_apu.m4 create mode 100644 2.5.13/2.5.x/apache2/build/find_curl.m4 create mode 100644 2.5.13/2.5.x/apache2/build/find_lua.m4 create mode 100644 2.5.13/2.5.x/apache2/build/find_pcre.m4 create mode 100644 2.5.13/2.5.x/apache2/build/find_xml.m4 create mode 100755 2.5.13/2.5.x/apache2/build/install-sh create mode 100644 2.5.13/2.5.x/apache2/build/libtool.m4 create mode 100755 2.5.13/2.5.x/apache2/build/ltmain.sh create mode 100755 2.5.13/2.5.x/apache2/configure create mode 100644 2.5.13/2.5.x/apache2/configure.in create mode 100644 2.5.13/2.5.x/apache2/mlogc-src/INSTALL create mode 100755 2.5.13/2.5.x/apache2/mlogc-src/Makefile.in create mode 100755 2.5.13/2.5.x/apache2/mlogc-src/Makefile.win create mode 100755 2.5.13/2.5.x/apache2/mlogc-src/mlogc-batch-load.pl.in create mode 100644 2.5.13/2.5.x/apache2/mlogc-src/mlogc-default.conf create mode 100644 2.5.13/2.5.x/apache2/mlogc-src/mlogc.c create mode 100644 2.5.13/2.5.x/apache2/mod_security2.c create mode 100644 2.5.13/2.5.x/apache2/mod_security2_config.h.in create mode 100644 2.5.13/2.5.x/apache2/mod_security2_config.hw create mode 100644 2.5.13/2.5.x/apache2/modsecurity.c create mode 100644 2.5.13/2.5.x/apache2/modsecurity.h create mode 100644 2.5.13/2.5.x/apache2/modules.mk create mode 100644 2.5.13/2.5.x/apache2/msc_geo.c create mode 100644 2.5.13/2.5.x/apache2/msc_geo.h create mode 100644 2.5.13/2.5.x/apache2/msc_logging.c create mode 100644 2.5.13/2.5.x/apache2/msc_logging.h create mode 100644 2.5.13/2.5.x/apache2/msc_lua.c create mode 100644 2.5.13/2.5.x/apache2/msc_lua.h create mode 100644 2.5.13/2.5.x/apache2/msc_multipart.c create mode 100644 2.5.13/2.5.x/apache2/msc_multipart.h create mode 100644 2.5.13/2.5.x/apache2/msc_parsers.c create mode 100644 2.5.13/2.5.x/apache2/msc_parsers.h create mode 100644 2.5.13/2.5.x/apache2/msc_pcre.c create mode 100644 2.5.13/2.5.x/apache2/msc_pcre.h create mode 100644 2.5.13/2.5.x/apache2/msc_release.c create mode 100644 2.5.13/2.5.x/apache2/msc_release.h create mode 100644 2.5.13/2.5.x/apache2/msc_reqbody.c create mode 100644 2.5.13/2.5.x/apache2/msc_test.c create mode 100644 2.5.13/2.5.x/apache2/msc_util.c create mode 100644 2.5.13/2.5.x/apache2/msc_util.h create mode 100644 2.5.13/2.5.x/apache2/msc_xml.c create mode 100644 2.5.13/2.5.x/apache2/msc_xml.h create mode 100644 2.5.13/2.5.x/apache2/pdf_protect.c create mode 100644 2.5.13/2.5.x/apache2/pdf_protect.h create mode 100644 2.5.13/2.5.x/apache2/persist_dbm.c create mode 100644 2.5.13/2.5.x/apache2/persist_dbm.h create mode 100644 2.5.13/2.5.x/apache2/re.c create mode 100644 2.5.13/2.5.x/apache2/re.h create mode 100644 2.5.13/2.5.x/apache2/re_actions.c create mode 100644 2.5.13/2.5.x/apache2/re_operators.c create mode 100644 2.5.13/2.5.x/apache2/re_tfns.c create mode 100644 2.5.13/2.5.x/apache2/re_variables.c create mode 100644 2.5.13/2.5.x/apache2/t/action/.empty create mode 100755 2.5.13/2.5.x/apache2/t/csv_rx-pm.pl.in create mode 100755 2.5.13/2.5.x/apache2/t/gen_rx-pm.pl.in create mode 100644 2.5.13/2.5.x/apache2/t/op/beginsWith.t create mode 100644 2.5.13/2.5.x/apache2/t/op/contains.t create mode 100644 2.5.13/2.5.x/apache2/t/op/containsWord.t create mode 100644 2.5.13/2.5.x/apache2/t/op/endsWith.t create mode 100644 2.5.13/2.5.x/apache2/t/op/eq.t create mode 100644 2.5.13/2.5.x/apache2/t/op/ge.t create mode 100644 2.5.13/2.5.x/apache2/t/op/geoLookup.t create mode 100644 2.5.13/2.5.x/apache2/t/op/gt.t create mode 100644 2.5.13/2.5.x/apache2/t/op/inspectFile.t create mode 100644 2.5.13/2.5.x/apache2/t/op/le.t create mode 100644 2.5.13/2.5.x/apache2/t/op/lt.t create mode 100644 2.5.13/2.5.x/apache2/t/op/m.t create mode 100644 2.5.13/2.5.x/apache2/t/op/noMatch.t create mode 100644 2.5.13/2.5.x/apache2/t/op/pm.t create mode 100644 2.5.13/2.5.x/apache2/t/op/pmFromFile-01.dat create mode 100644 2.5.13/2.5.x/apache2/t/op/pmFromFile.t create mode 100644 2.5.13/2.5.x/apache2/t/op/rbl.t create mode 100644 2.5.13/2.5.x/apache2/t/op/rx.t create mode 100644 2.5.13/2.5.x/apache2/t/op/streq.t create mode 100644 2.5.13/2.5.x/apache2/t/op/unconditionalMatch.t create mode 100644 2.5.13/2.5.x/apache2/t/op/validateByteRange.t create mode 100644 2.5.13/2.5.x/apache2/t/op/validateDTD.t create mode 100644 2.5.13/2.5.x/apache2/t/op/validateSchema.t create mode 100644 2.5.13/2.5.x/apache2/t/op/validateUrlEncoding.t create mode 100644 2.5.13/2.5.x/apache2/t/op/validateUtf8Encoding.t create mode 100644 2.5.13/2.5.x/apache2/t/op/verifyCC.t create mode 100644 2.5.13/2.5.x/apache2/t/op/within.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/action/00-disruptive-actions.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/action/00-meta.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/action/00-misc.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/action/00-transformations.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/action/10-append-prepend.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/action/10-ctl.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/action/10-detectiononly-actions.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/action/10-logging.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/config/00-load-modsec.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/config/10-audit-directives.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/config/10-debug-directives.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/config/10-misc-directives.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/config/10-request-directives.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/config/10-response-directives.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/config/20-chroot.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/misc/00-multipart-parser.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/misc/00-phases.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/misc/10-pcre.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/misc/10-tfn-cache.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/misc/20-pdf-xss.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/rule/00-basics.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/rule/00-inheritance.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/rule/00-script.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/rule/10-xml.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/rule/20-exceptions.t create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope-bad.dtd create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope-bad.xsd create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope.dtd create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope.xsd create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/conf/httpd.conf.in create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/conf/match.lua create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/conf/test.lua create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/data/.empty create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/8k.txt create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/index.html create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/test.pdf create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/test.txt create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/test2.txt create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/logs/audit/.empty create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/logs/subdir/.empty create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/tmp/.empty create mode 100644 2.5.13/2.5.x/apache2/t/regression/server_root/upload/.empty create mode 100644 2.5.13/2.5.x/apache2/t/regression/target/00-targets.t create mode 100755 2.5.13/2.5.x/apache2/t/run-regression-tests.pl.in create mode 100755 2.5.13/2.5.x/apache2/t/run-unit-tests.pl.in create mode 100644 2.5.13/2.5.x/apache2/t/tfn/base64Decode.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/base64Encode.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/compressWhitespace.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/cssDecode.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/escapeSeqDecode.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/hexDecode.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/hexEncode.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/htmlEntityDecode.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/jsDecode.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/length.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/lowercase.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/md5.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/normalisePath.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/normalisePathWin.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/parityEven7bit.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/parityOdd7bit.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/parityZero7bit.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/removeNulls.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/removeWhitespace.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/replaceComments.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/replaceNulls.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/sha1.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/trim.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/trimLeft.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/trimRight.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/urlDecode.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/urlDecodeUni.t create mode 100644 2.5.13/2.5.x/apache2/t/tfn/urlEncode.t create mode 100644 2.5.13/2.5.x/apache2/utf8tables.h create mode 100644 2.5.13/2.5.x/authors.txt create mode 100644 2.5.13/2.5.x/doc/Makefile create mode 100644 2.5.13/2.5.x/doc/apache_request_cycle-modsecurity.jpg create mode 100644 2.5.13/2.5.x/doc/html-chunked.xsl create mode 100644 2.5.13/2.5.x/doc/html.xsl create mode 100644 2.5.13/2.5.x/doc/main-index.html create mode 100644 2.5.13/2.5.x/doc/migration-matrix.html create mode 100644 2.5.13/2.5.x/doc/migration-matrix.xml create mode 100644 2.5.13/2.5.x/doc/modsecurity-logo.png create mode 100644 2.5.13/2.5.x/doc/modsecurity-reference.css create mode 100644 2.5.13/2.5.x/doc/modsecurity.gif create mode 100644 2.5.13/2.5.x/doc/modsecurity2-apache-reference.xml create mode 100644 2.5.13/2.5.x/doc/modsecurity2-data-formats.xml create mode 100644 2.5.13/2.5.x/doc/pdf.xsl create mode 100644 2.5.13/2.5.x/doc/trustwave-logo-small.gif create mode 100644 2.5.13/2.5.x/modsecurity.conf-minimal create mode 100644 2.5.13/2.5.x/tools/README create mode 100644 2.5.13/2.5.x/tools/rules-updater-example.conf create mode 100644 2.5.13/2.5.x/tools/rules-updater.pl.in diff --git a/2.5.13/2.5.x/CHANGES b/2.5.13/2.5.x/CHANGES new file mode 100644 index 00000000..21eb72fc --- /dev/null +++ b/2.5.13/2.5.x/CHANGES @@ -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: action to allow skipping all rules until a rule + with a specified ID is reached. Rule execution then continues after + the specified rule. + + * Added SecMarker directive to allow a fixed target for skipAfter. + + * Added ctl:ruleRemoveById action to allow rule removal on a match. + + * Added a @containsWord operator that will match a given string anywhere in + the target value, but only on word boundaries. + + * Added a MATCHED_VAR_NAME variable to store the last matched variable name + so that it can be more easily used by rules. + + * Added a MATCHED_VAR variable to store the last matched variable value + so that it can be more easily used by rules. + + * Fixed expansion of macros when using relative changes with setvar. In + addition, added support for expanding macros in the variable name. + + * Situations where ModSecurity will intercept, generate an error or log + a level 1-3 message to the debug log are now marked as 'relevant' and may + generate an audit log entry. + + * Fixed deprecatevar:var=N/S action so that it decrements N every S seconds + as documented instead of decrementing by a rate. + + * Enable ModSecurity to look at partial response bodies. In previous + versions, ModSecurity would respond with status code 500 when the + response body was too long. Now, if SecResponseBodyLimitAction is + set to "ProcessPartial", it will process the part of the response + body received up until that point but send the rest without buffering. + + * ModSecurity will now process phases 3 and 4 even when request processing + is interrupted (either by Apache - e.g. by responding with 400, 401 + or 403, or by ModSecurity itself). + + * Fixed the base64decode transformation function to not return extra + characters at the end. + + * Return from the output filter with an error in addition to setting + up the HTTP error status in the output data. + + * Used new Apache API calls to get the server version/banner when available. + + * Added "logdata" meta action to allow logging of raw transaction data. + + * Added TX_SEVERITY that keeps track of the highest severity + for any matched rules so far. + + * Added ARGS_GET, ARGS_POST, ARGS_GET_NAMES, ARGS_POST_NAMES variables to + allow seperation of GET and POST arguments. + + * Added an Apache define (MODSEC_2.5) so that you can conditionally include + directives based on the ModSecurity major/minor versions with IfDefine. + + * Added MODSEC_BUILD variable that contains the numeric build value based + on the ModSecurity version. + + * Enhanced debug logging by displaying more data on rule execution. All + invoked rules are now logged in the debug log at level 5. + + * Stricter validation for @validateUtf8Encoding. + + * No longer process Apache internal subrequests. + + * Fixed warnings on Solaris and/or 64bit builds. + + * Added @within string comparison operator with support for macro expansion. + + * Do not trigger "pause" action for internal requests. + + * Added matching rule filename and line number to audit log. + + * Added new phrase matching operators, @pm and @pmFromFile. These use + an alternate set based matching engine (Aho-Corasick) to perform faster + phrase type matches such as black/white lists, spam keywords, etc. + + * Allow caching transformations per-request/phase so they are not repeated. + + * Added Solaris and Cygwin to the list of platforms not supporting the hidden + visibility attribute. + + * Fixed decoding full-width unicode in t:urlDecodeUni. + + * Add SecGeoLookupDB, @geoLookups and GEO collection to support + geographical lookups by IP/host. + + * Do not try to intercept a request after a failed rule. This fixes the + issue associated with an "Internal Error: Asked to intercept request + but was_intercepted is zero" error message. + + * Removed extraneous exported symbols. + + * Merged the PDF XSS protection functionality into ModSecurity. + + * Exported API for registering custom variables. Example in api directory. + + * Added experimental support for content injection. Directive + SecContentInjection (On|Off) controls whether injection is taking place. + Actions "prepend" and "append" inject content when executed. Do note that + it is your responsibility to make sure the response is of the appropriate + content type (e.g. HTML, plain text, etc). + + * Added string comparison operators with support for macro expansion: + @contains, @streq, @beginsWith and @endsWith. + + * Enhanced debug log output to log macro expansion, quote values and + correctly display values that contained NULs. + + * Removed support for %0 - %9 capture macros as they were incorrectly + expanding url encoded values. Use %{TX.0} - %{TX.9} instead. + + * Added t:length to transform a value to its character length. + + * Added t:trimLeft, t:trimRight, t:trim to remove whitespace + from a value on the left, right or both. + + * Added SecAuditLog2 directive to allow redundent concurrent audit log + index files. This will allow sending audit data to two consoles, etc. + + * Removed CGI style HTTP_* variables in favor of REQUEST_HEADERS:Header-Name. + + * Store filename/line for each rule and display it and the ID (if available) + in the debug log when invoking a rule. Thanks to Christian Bockermann + for the idea. + + * Do not log 'allow' action as intercepted in the debug log. + + * Fixed some collection variable names not printing with the parameter + and/or counting operator in the debug log. + + +19 Feb 2008 - 2.1.6 +------------------- + + * Fixed crash on startup when ENV is improperly used without a parameter. + + * Allow macro resolution in setenv action. + + * Implemented SecUploadFileMode to allow setting the mode for uploaded files. + + * No longer log the query portion of the URI in the error log as + it may contain sensitive data. + + +10 Jan 2008 - 2.1.5 +------------------- + + * Updated included Core Ruleset to version 1.5.1. + + * Phase 5 rules can now be removed via SecRuleRemoveBy* directives. + + * Fixed issue where only the first phase 5 rule would run when the + request was intercepted in an earlier phase. + + * Fixed configuration parsing so that disruptive actions, meta actions + and phases are not allowed in a chained rule (as originally intended). + + * Fixed t:escapeSeqDecode to better follow ANSI C escapes. + + +27 Nov 2007 - 2.1.4 +------------------- + + * Updated included Core Ruleset to version 1.5 and noted in the docs that + XML support is required to use the rules without modification. + + * Fixed an evasion FP, mistaking a multipart non-boundary for a boundary. + + * Fixed multiple warnings on Solaris and/or 64bit builds. + + * Do not process subrequests in phase 2-4, but do hand off the request data. + + * Fixed a blocking FP in the multipart parser, which affected Safari. + + +11 Sep 2007 - 2.1.3 +------------------- + + * Updated multipart parsing code adding variables to allow checking + for various parsing issues (request body abnormalities). + + * Allow mod_rpaf and mod_extract_forwarded2 to work before ModSecurity. + + * Quiet some compiler warnings. + + * Do not block internal ErrorDocument requests after blocking request. + + * Added ability to compile without an external API (use -DNO_MODSEC_API). + + +27 Jul 2007 - 2.1.2 +------------------- + + * Cleaned up and clarified some documentation. + + * Update included core rules to latest version (1.4.3). + + * Enhanced ability to alert/audit failed requests. + + * Do not trigger "pause" action for internal requests. + + * Fixed issue with requests that use internal requests. These had the + potential to be intercepted incorrectly when other Apache httpd modules + that used internal requests were used with mod_security. + + * Added Solaris and Cygwin to the list of platforms not supporting the hidden + visibility attribute. + + * Fixed decoding full-width unicode in t:urlDecodeUni. + + * Lessen some overhead of debugging messages and calculations. + + * Do not try to intercept a request after a failed rule. This fixes the + issue associated with an "Internal Error: Asked to intercept request + but was_intercepted is zero" error message. + + * Added SecAuditLog2 directive to allow redundent concurrent audit log + index files. This will allow sending audit data to two consoles, etc. + + * Small performance improvement in memory management for rule execution. + + +11 Apr 2007 - 2.1.1 +------------------- + + * Add the PCRE_DOLLAR_ENDONLY option when compiling regular expression + for the @rx operator and variables. + + * Really set PCRE_DOTALL option when compiling the regular expression + for the @rx operator as the docs state. + + * Fixed potential memory corruption when expanding macros. + + * Fixed error when a collection was retrieved from storage in the same second + as creation by setting the rate to zero. + + * Fixed ASCIIZ (NUL) parsing for application/x-www-form-urlencoded forms. + + * Fixed the faulty REQUEST_FILENAME variable, which used to change + the internal Apache structures by mistake. + + * Updates to quiet some compiler warnings. + + * Fixed some casting issues for compiling on NetWare (patch from Guenter Knauf). + + +23 Feb 2007 - 2.1.0 +------------------- + + * Removed the "Connection reset by peer" message, which has nothing + to do with us. Actually the message was downgraded from ERROR to + NOTICE so it will still appear in the debug log. + + * Removed the (harmless) message mentioning LAST_UPDATE_TIME missing. + + * It was not possible to remove a rule placed in phase 4 using + SecRuleRemoveById or SecRuleRemoveByMsg. Fixed. + + * Fixed a problem with incorrectly setting requestBodyProcessor using + the ctl action. + + * Bundled Core Rules 2.1-1.3.2b4. + + * Updates to the reference manual. + + * Reversed the return values of @validateDTD and @validateSchema, to + make them consistent with other operators. + + * Added a few helpful debug messages in the XML validation area. + + * Updates to the reference manual. + + * Fixed the validateByteRange operator. + + * Default value for the status action is now 403 (as it was supposed to + be but it was effectively 500). + + * Rule exceptions (removing using an ID range or an regular expression) + is now applied to the current context too. (Previously it only worked + on rules that are inherited from the parent context.) + + * Fix of a bug with expired variables. + + * Fixed regular expression variable selectors for many collections. + + * Performance improvements - up to two times for real-life work loads! + + * Memory consumption improvements (not measured but significant). + + * The allow action did not work in phases 3 and 4. Fixed. + + * Unlocked collections GLOBAL and RESOURCE. + + * Added support for variable expansion in the msg action. + + * New feature: It is now possible to make relative changes to the + audit log parts with the ctl action. For example: "ctl:auditLogParts=+E". + + * New feature: "tag" action. To be used for event categorisation. + + * XML parser was not reporting errors that occured at the end + of XML payload. + + * Files were not extracted from request if SecUploadKeepFiles was + Off. Fixed. + + * Regular expressions that are too long are truncated to 256 + characters before used in error messages. (In order to keep + the error messages in the log at a reasonable size.) + + * Fixed the sha1 transformation function. + + * Fixed the skip action. + + * Fixed REQUEST_PROTOCOL, REMOTE_USER, and AUTH_TYPE. + + * SecRuleEngine did not work in child configuration contexts + (e.g. ). + + * Fixed base64Decode and base64Encode. + + +15 Nov 2006 - 2.0.4 +------------------- + + * Fixed the "deprecatevar" action. + + * Decreasing variable values did not work. + + * Made "nolog" do what it is supposed to do - cause a rule match to + not be logged. Also "nolog" now implies "noauditlog" but it's + possible to follow "nolog" with "auditlog" and have the match + not logged to the error log but logged to the auditlog. (Not + something that strikes me as useful but it's possible.) + + * Relative paths given to SecDataDir will now be treated as relative + to the Apache server root. + + * Added checks to make sure only correct actions are specified in + SecDefaultAction (some actions are required, some don't make any + sense) and in rules that are not chain starters (same). This should + make the unhelpful "Internal Error: Failed to add rule to the ruleset" + message go away. + + * Fixed the problem when "SecRuleInheritance Off" is used in a context + with no rules defined. + + * Fixed a problem of lost input (request body) data on some redirections, + for example when mod_rewrite is used. + + +26 Oct 2006 - 2.0.3 +------------------- + + * Fixed a memory leak (all platforms) and a concurrency control + problem that could cause a crash (multithreaded platforms only). + + * Fixed a SecAuditLogRelevantStatus problem, which would not work + properly unless the regular expression contained a subexpression. + + +19 Oct 2006 - 2.0.2 +------------------- + + * Fixed incorrect permissions on the global mutex, which prevented + the mutex from working properly. + + * Fixed incorrect actionset merging where the status was copied from + the child actionset even though it was not defined. + + * Fixed missing metadata information (in the logs) for warnings. + + +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. diff --git a/2.5.13/2.5.x/LICENSE b/2.5.13/2.5.x/LICENSE new file mode 100644 index 00000000..0cd26d5b --- /dev/null +++ b/2.5.13/2.5.x/LICENSE @@ -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 + diff --git a/2.5.13/2.5.x/MODSECURITY_LICENSING_EXCEPTION b/2.5.13/2.5.x/MODSECURITY_LICENSING_EXCEPTION new file mode 100644 index 00000000..c31a655d --- /dev/null +++ b/2.5.13/2.5.x/MODSECURITY_LICENSING_EXCEPTION @@ -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 + diff --git a/2.5.13/2.5.x/README.TXT b/2.5.13/2.5.x/README.TXT new file mode 100644 index 00000000..152a7619 --- /dev/null +++ b/2.5.13/2.5.x/README.TXT @@ -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. diff --git a/2.5.13/2.5.x/README_WINDOWS.TXT b/2.5.13/2.5.x/README_WINDOWS.TXT new file mode 100644 index 00000000..2993da24 --- /dev/null +++ b/2.5.13/2.5.x/README_WINDOWS.TXT @@ -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 + + 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" + + + 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". diff --git a/2.5.13/2.5.x/apache2/.deps b/2.5.13/2.5.x/apache2/.deps new file mode 100644 index 00000000..e69de29b diff --git a/2.5.13/2.5.x/apache2/Makefile.in b/2.5.13/2.5.x/apache2/Makefile.in new file mode 100644 index 00000000..c3aae466 --- /dev/null +++ b/2.5.13/2.5.x/apache2/Makefile.in @@ -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 diff --git a/2.5.13/2.5.x/apache2/Makefile.win b/2.5.13/2.5.x/apache2/Makefile.win new file mode 100644 index 00000000..95a7a3fb --- /dev/null +++ b/2.5.13/2.5.x/apache2/Makefile.win @@ -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 diff --git a/2.5.13/2.5.x/apache2/acmp.c b/2.5.13/2.5.x/apache2/acmp.c new file mode 100644 index 00000000..a8fae4af --- /dev/null +++ b/2.5.13/2.5.x/apache2/acmp.c @@ -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 +#define utf8_lcase(a) apr_tolower(a) +#endif + +#include +#include +#include + + +/* + ******************************************************************************* + ******************************************************************************* + * Data structures for acmp parser + */ + + /** + * One node in trie + */ +typedef struct acmp_node_t acmp_node_t; +typedef struct acmp_btree_node_t acmp_btree_node_t; +struct acmp_node_t { + acmp_utf8_char_t letter; + int is_last; + acmp_callback_t callback; + void *callback_data; + int depth; + + acmp_node_t *child; + acmp_node_t *sibling; + acmp_node_t *fail; + acmp_node_t *parent; + acmp_node_t *o_match; + + acmp_btree_node_t *btree; + + apr_size_t hit_count; + + char *text; + char *pattern; +}; + +struct acmp_btree_node_t { + acmp_utf8_char_t letter; + acmp_btree_node_t *left; + acmp_btree_node_t *right; + acmp_node_t *node; +}; + +/** + * Data related to parser, not to individual nodes + */ +struct ACMP { + #ifdef ACMP_USE_UTF8 + int is_utf8; + #endif + int is_case_sensitive; + apr_pool_t *parent_pool; + apr_pool_t *pool; + + int dict_count; + apr_size_t longest_entry; + + acmp_node_t *root_node; + + const char *data_start; + const char *data_end; + const char *data_pos; + apr_size_t data_len; + + apr_size_t *bp_buffer; + apr_size_t bp_buff_len; + + acmp_node_t *active_node; + char u8_buff[6]; + apr_size_t u8buff_len; + apr_size_t hit_count; + int is_failtree_done; + int is_active; + apr_size_t byte_pos; + apr_size_t char_pos; +}; + +/* + ******************************************************************************* + ******************************************************************************* + * Functions for UTF-8 support + */ + +#ifdef ACMP_USE_UTF8 +/** + * Returns length of utf-8 sequence based on its first byte + */ +static int utf8_seq_len(const char *first_byte) { + return utf8_seq_lengths[(unsigned int)(unsigned char)first_byte[0]]; +} + +/** + * Returns length of utf8-encoded text + */ +static size_t utf8_strlen(const char *str) { + int len = 0; + const char *c = str; + while (*c != 0) { + c += utf8_seq_len(c); + len++; + } + return len; +} + +/** + * Returns ucs code for given utf-8 sequence + */ +static acmp_utf8_char_t utf8_decodechar(const char *str) { + int len = utf8_seq_len(str); + acmp_utf8_char_t ch = 0; + switch (len) { + case 6: ch += (unsigned char)*str++; ch <<= 6; + case 5: ch += (unsigned char)*str++; ch <<= 6; + case 4: ch += (unsigned char)*str++; ch <<= 6; + case 3: ch += (unsigned char)*str++; ch <<= 6; + case 2: ch += (unsigned char)*str++; ch <<= 6; + case 1: ch += (unsigned char)*str++; + } + ch -= utf8_offsets[len - 1]; + return ch; +} + +/** + * Returns lowercase for given unicode character. Searches through + * utf8_lcase_map table, if it doesn't find the code assumes + * it doesn't have a lowercase variant and returns code itself. + */ +static long utf8_lcase(acmp_utf8_char_t ucs_code) { + long mid, left, right; + left = 1; + right = UTF8_LCASEMAP_LEN * 2 + 1; + + while (left <= right) { + mid = (left + right) >> 1; + mid -= (mid % 2); mid++; + if (ucs_code > utf8_lcase_map[mid]) + left = mid + 2; + else if (ucs_code < utf8_lcase_map[mid]) + right = mid - 2; + else if (ucs_code == utf8_lcase_map[mid]) + return utf8_lcase_map[mid - 1]; + } + return ucs_code; +} +#endif + +/* + ******************************************************************************* + ******************************************************************************* + * Code for local / static utility functions + */ + +/** + * Returns length of given string for parser's encoding + */ +static size_t acmp_strlen(ACMP *parser, const char *str) { + #ifdef ACMP_USE_UTF8 + return (parser->is_utf8 == 0) ? strlen(str) : utf8_strlen(str); + #else + return strlen(str); + #endif +} + +/** + * Turns string to array of ucs values, depending on parser's encoding + * str - string to convert, doesn't have to be NULL-terminated + * ucs_chars - where to write ucs values + * len - length of input string + */ +static void acmp_strtoucs(ACMP *parser, const char *str, acmp_utf8_char_t *ucs_chars, int len) { + int i; + const char *c = str; + + #ifdef ACMP_USE_UTF8 + if (parser->is_utf8) { + for (i = 0; i < len; i++) { + *(ucs_chars++) = utf8_decodechar(c); + c += utf8_seq_len(c); + } + } else + #endif + { + for (i = 0; i < len; i++) { + *(ucs_chars++) = *(c++); + } + } +} + +/** + * Returns node with given letter, or null if not found + */ +static acmp_node_t *acmp_child_for_code(acmp_node_t *parent_node, acmp_utf8_char_t ucs_code) { + acmp_node_t *node = parent_node->child; + if (node == NULL) return NULL; + for (;;) { + if (node->letter == ucs_code) return node; + node = node->sibling; + if (node == NULL) return NULL; + } +} + +/** + * Adds node to parent node, if it is not already there + */ +static void acmp_add_node_to_parent(acmp_node_t *parent, acmp_node_t *child) { + acmp_node_t *node = NULL; + + child->parent = parent; + if (parent->child == NULL) { + parent->child = child; + return; + } + + node = parent->child; + for (;;) { + if (node == child) return; + if (node->sibling == NULL) { + node->sibling = child; + return; + } + node = node->sibling; + } +} + +/** + * Copies values from one node to another, without child/sibling/fail pointers + * and without state variables. + */ +static void acmp_clone_node_no_state(acmp_node_t *from, acmp_node_t *to) { + memcpy(to, from, sizeof(acmp_node_t)); + to->child = NULL; + to->sibling = NULL; + to->fail = NULL; + to->hit_count = 0; +} + +/** + * 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; +} diff --git a/2.5.13/2.5.x/apache2/acmp.h b/2.5.13/2.5.x/apache2/acmp.h new file mode 100644 index 00000000..dcd473d9 --- /dev/null +++ b/2.5.13/2.5.x/apache2/acmp.h @@ -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 +#include + +#define ACMP_FLAG_BYTE 0 +#define ACMP_FLAG_CASE_SENSITIVE 1 +#define ACMP_FLAG_CASE_INSENSITIVE 0 +#ifdef ACMP_USE_UTF8 +#define ACMP_FLAG_UTF8 0x100 +#endif + +/** + * Opaque struct with parser data + */ +typedef struct ACMP ACMP; + +/** + * Used to separate state from the trie for acmp_process_quick function + */ +typedef struct { + ACMP *parser; + void *ptr; +} ACMPT; + +/** + * Callback function. Arguments are: + * ACMP * - acmp parser that initiated callback + * void * - custom data you supplied when adding callback + * apr_size_t - position in bytes where pattern was found + * apr_size_t - position in chars where pattern was found, for multibyte strings + */ +typedef void (*acmp_callback_t)(ACMP *, void *, apr_size_t, apr_size_t); + +/** + * flags - OR-ed values of ACMP_FLAG constants + * pool - apr_pool to use as parent pool, can be set to NULL + */ +ACMP *acmp_create(int flags, apr_pool_t *pool); + +/** + * Destroys previously created parser + */ +void acmp_destroy(ACMP *parser); + +/** + * Creates parser with same options and same patterns + * parser - ACMP parser to duplicate + * pool - parent pool to use, if left as NULL original parser's parent pool is used + */ +ACMP *acmp_duplicate(ACMP *parser, apr_pool_t *pool); + +/** + * Adds pattern to parser. Cannot be done after starting the search. + * parser - ACMP parser + * pattern - string with pattern to match + * callback - Optional, pointer to an acmp_callback_t function + * data - pointer to data that will be passed to callback function, only used if callback + * is supplied + * len - Length of pattern in characters, if zero string length is used. + */ +apr_status_t acmp_add_pattern(ACMP *parser, const char *pattern, + acmp_callback_t callback, void *data, apr_size_t len); + +/** + * Called to process incoming data stream. You must call acmp_done after sending + * last data packet + * + * data - ptr to incoming data + * len - size of data in bytes + */ +apr_status_t acmp_process(ACMP *parser, const char *data, apr_size_t len); + +/** + * Returns number of matches on all patterns combined + */ +apr_size_t acmp_match_count_total(ACMP *parser); + +/** + * Returns number of matches for given pattern + */ +apr_size_t acmp_match_count(ACMP *parser, const char *pattern); + +/** + * Resets the state of parser so you can start using it with new set of data, + * or add new patterns. + */ +void acmp_reset(ACMP *parser); + +/** + * Creates an ACMPT struct that will use parser's tree, without duplicating its data + */ +ACMPT *acmp_duplicate_quick(ACMP *parser, apr_pool_t *pool); + +/** + * Process the data using ACMPT to keep state, and ACMPT's parser to keep the tree + */ +apr_status_t acmp_process_quick(ACMPT *acmpt, const char **match, const char *data, apr_size_t len); + +/** + * Prepares parser for searching + */ +apr_status_t acmp_prepare(ACMP *parser); + + +#endif /*ACMP_H_*/ diff --git a/2.5.13/2.5.x/apache2/apache2.h b/2.5.13/2.5.x/apache2/apache2.h new file mode 100644 index 00000000..e9b4a9e7 --- /dev/null +++ b/2.5.13/2.5.x/apache2/apache2.h @@ -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 +#include + + +#if (!defined(NO_MODSEC_API)) +/* Optional functions. */ + +APR_DECLARE_OPTIONAL_FN(void, modsec_register_tfn, (const char *name, void *fn)); +APR_DECLARE_OPTIONAL_FN(void, modsec_register_operator, (const char *name, void *fn_init, void *fn_exec)); +APR_DECLARE_OPTIONAL_FN(void, modsec_register_variable, + (const char *name, unsigned int type, + unsigned int argc_min, unsigned int argc_max, + void *fn_validate, void *fn_generate, + unsigned int is_cacheable, unsigned int availability)); +#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 + diff --git a/2.5.13/2.5.x/apache2/apache2_config.c b/2.5.13/2.5.x/apache2/apache2_config.c new file mode 100644 index 00000000..70b4ece3 --- /dev/null +++ b/2.5.13/2.5.x/apache2/apache2_config.c @@ -0,0 +1,2432 @@ +/* + * 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 + +#include "modsecurity.h" +#include "msc_logging.h" +#include "msc_util.h" +#include "pdf_protect.h" +#include "http_log.h" + +#if defined(WITH_LUA) +#include "msc_lua.h" +#endif + + +/* -- Directory context creation and initialisation -- */ + +/** + * Creates a fresh directory configuration. + */ +void *create_directory_config(apr_pool_t *mp, char *path) +{ + directory_config *dcfg = (directory_config *)apr_pcalloc(mp, sizeof(directory_config)); + if (dcfg == NULL) return NULL; + + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Created directory config %pp path %s", dcfg, path); + #endif + + dcfg->mp = mp; + dcfg->is_enabled = NOT_SET; + + dcfg->reqbody_access = NOT_SET; + dcfg->reqbody_buffering = NOT_SET; + dcfg->reqbody_inmemory_limit = NOT_SET; + dcfg->reqbody_limit = NOT_SET; + dcfg->reqbody_no_files_limit = NOT_SET; + dcfg->resbody_access = NOT_SET; + + dcfg->debuglog_name = NOT_SET_P; + dcfg->debuglog_level = NOT_SET; + dcfg->debuglog_fd = NOT_SET_P; + + dcfg->of_limit = NOT_SET; + dcfg->of_limit_action = NOT_SET; + dcfg->of_mime_types = NOT_SET_P; + dcfg->of_mime_types_cleared = NOT_SET; + + dcfg->cookie_format = NOT_SET; + dcfg->argument_separator = NOT_SET; + + dcfg->rule_inheritance = NOT_SET; + dcfg->rule_exceptions = apr_array_make(mp, 16, sizeof(rule_exception *)); + + /* audit log variables */ + dcfg->auditlog_flag = NOT_SET; + dcfg->auditlog_type = NOT_SET; + dcfg->auditlog_dirperms = NOT_SET; + dcfg->auditlog_fileperms = NOT_SET; + dcfg->auditlog_name = NOT_SET_P; + dcfg->auditlog2_name = NOT_SET_P; + dcfg->auditlog_fd = NOT_SET_P; + dcfg->auditlog2_fd = NOT_SET_P; + dcfg->auditlog_storage_dir = NOT_SET_P; + dcfg->auditlog_parts = NOT_SET_P; + dcfg->auditlog_relevant_regex = NOT_SET_P; + + dcfg->ruleset = NULL; + + /* Upload */ + dcfg->tmp_dir = NOT_SET_P; + dcfg->upload_dir = NOT_SET_P; + dcfg->upload_keep_files = NOT_SET; + dcfg->upload_validates_files = NOT_SET; + dcfg->upload_filemode = NOT_SET; + dcfg->upload_file_limit = NOT_SET; + + /* These are only used during the configuration process. */ + dcfg->tmp_chain_starter = NULL; + dcfg->tmp_default_actionset = NULL; + dcfg->tmp_rule_placeholders = NULL; + + /* Misc */ + dcfg->data_dir = NOT_SET_P; + dcfg->webappid = NOT_SET_P; + + /* Content injection. */ + dcfg->content_injection_enabled = NOT_SET; + + /* PDF XSS protection. */ + dcfg->pdfp_enabled = NOT_SET; + dcfg->pdfp_secret = NOT_SET_P; + dcfg->pdfp_timeout = NOT_SET; + dcfg->pdfp_token_name = NOT_SET_P; + dcfg->pdfp_only_get = NOT_SET; + dcfg->pdfp_method = NOT_SET; + + /* Geo Lookups */ + dcfg->geo = NOT_SET_P; + + /* Cache */ + dcfg->cache_trans = NOT_SET; + dcfg->cache_trans_incremental = NOT_SET; + dcfg->cache_trans_min = NOT_SET; + dcfg->cache_trans_max = NOT_SET; + dcfg->cache_trans_maxitems = NOT_SET; + + dcfg->component_signatures = apr_array_make(mp, 16, sizeof(char *)); + + dcfg->request_encoding = NOT_SET_P; + + return dcfg; +} + +/** + * Copies rules between one phase of two configuration contexts, + * taking exceptions into account. + */ +static void copy_rules_phase(apr_pool_t *mp, + apr_array_header_t *parent_phase_arr, + apr_array_header_t *child_phase_arr, + apr_array_header_t *exceptions_arr) +{ + rule_exception **exceptions; + msre_rule **rules; + int i, j; + int mode = 0; + + rules = (msre_rule **)parent_phase_arr->elts; + for(i = 0; i < parent_phase_arr->nelts; i++) { + msre_rule *rule = (msre_rule *)rules[i]; + int copy = 1; + + if (mode == 0) { + /* First rule in the chain. */ + exceptions = (rule_exception **)exceptions_arr->elts; + for(j = 0; j < exceptions_arr->nelts; j++) { + + /* Process exceptions. */ + switch(exceptions[j]->type) { + case RULE_EXCEPTION_REMOVE_ID : + if ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) { + int ruleid = atoi(rule->actionset->id); + if (rule_id_in_range(ruleid, exceptions[j]->param)) copy--; + } + break; + case RULE_EXCEPTION_REMOVE_MSG : + if ((rule->actionset != NULL)&&(rule->actionset->msg != NULL)) { + char *my_error_msg = NULL; + + int rc = msc_regexec(exceptions[j]->param_data, + rule->actionset->msg, strlen(rule->actionset->msg), + &my_error_msg); + if (rc >= 0) copy--; + } + break; + } + } + + if (copy > 0) { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy rule %pp [id \"%s\"]", rule, rule->actionset->id); + #endif + + /* Copy the rule. */ + *(msre_rule **)apr_array_push(child_phase_arr) = rule; + if (rule->actionset->is_chained) mode = 2; + } else { + if (rule->actionset->is_chained) mode = 1; + } + } else { + if (mode == 2) { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy chain %pp for rule %pp [id \"%s\"]", rule, rule->chain_starter, rule->chain_starter->actionset->id); + #endif + + /* Copy the rule (it belongs to the chain we want to include. */ + *(msre_rule **)apr_array_push(child_phase_arr) = rule; + } + + if ((rule->actionset == NULL)||(rule->actionset->is_chained == 0)) mode = 0; + } + } +} + +/** + * Copies rules between two configuration contexts, + * taking exceptions into account. + */ +static int copy_rules(apr_pool_t *mp, msre_ruleset *parent_ruleset, + msre_ruleset *child_ruleset, + apr_array_header_t *exceptions_arr) +{ + copy_rules_phase(mp, parent_ruleset->phase_request_headers, + child_ruleset->phase_request_headers, exceptions_arr); + copy_rules_phase(mp, parent_ruleset->phase_request_body, + child_ruleset->phase_request_body, exceptions_arr); + copy_rules_phase(mp, parent_ruleset->phase_response_headers, + child_ruleset->phase_response_headers, exceptions_arr); + copy_rules_phase(mp, parent_ruleset->phase_response_body, + child_ruleset->phase_response_body, exceptions_arr); + copy_rules_phase(mp, parent_ruleset->phase_logging, + child_ruleset->phase_logging, exceptions_arr); + + return 1; +} + +/** + * Merges two directory configurations. + */ +void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) +{ + directory_config *parent = (directory_config *)_parent; + directory_config *child = (directory_config *)_child; + directory_config *merged = create_directory_config(mp, NULL); + + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Merge parent %pp child %pp RESULT %pp", _parent, _child, merged); + #endif + + if (merged == NULL) return NULL; + + /* Use values from the child configuration where possible, + * otherwise use the parent's. + */ + + merged->is_enabled = (child->is_enabled == NOT_SET + ? parent->is_enabled : child->is_enabled); + + /* IO parameters */ + merged->reqbody_access = (child->reqbody_access == NOT_SET + ? parent->reqbody_access : child->reqbody_access); + merged->reqbody_buffering = (child->reqbody_buffering == NOT_SET + ? parent->reqbody_buffering : child->reqbody_buffering); + merged->reqbody_inmemory_limit = (child->reqbody_inmemory_limit == NOT_SET + ? parent->reqbody_inmemory_limit : child->reqbody_inmemory_limit); + merged->reqbody_limit = (child->reqbody_limit == NOT_SET + ? parent->reqbody_limit : child->reqbody_limit); + merged->reqbody_no_files_limit = (child->reqbody_no_files_limit == NOT_SET + ? parent->reqbody_no_files_limit : child->reqbody_no_files_limit); + merged->resbody_access = (child->resbody_access == NOT_SET + ? parent->resbody_access : child->resbody_access); + + merged->of_limit = (child->of_limit == NOT_SET + ? parent->of_limit : child->of_limit); + merged->of_limit_action = (child->of_limit_action == NOT_SET + ? parent->of_limit_action : child->of_limit_action); + + if (child->of_mime_types != NOT_SET_P) { + /* Child added to the table */ + + if (child->of_mime_types_cleared == 1) { + /* The list of MIME types was cleared in the child, + * which means the parent's MIME types went away and + * we should not take them into consideration here. + */ + merged->of_mime_types = child->of_mime_types; + merged->of_mime_types_cleared = 1; + } else { + /* Add MIME types defined in the child to those + * defined in the parent context. + */ + if (parent->of_mime_types == NOT_SET_P) { + merged->of_mime_types = child->of_mime_types; + merged->of_mime_types_cleared = NOT_SET; + } else { + merged->of_mime_types = apr_table_overlay(mp, parent->of_mime_types, + child->of_mime_types); + if (merged->of_mime_types == NULL) return NULL; + } + } + } else { + /* Child did not add to the table */ + + if (child->of_mime_types_cleared == 1) { + merged->of_mime_types_cleared = 1; + } else { + merged->of_mime_types = parent->of_mime_types; + merged->of_mime_types_cleared = parent->of_mime_types_cleared; + } + } + + /* debug log */ + if (child->debuglog_fd == NOT_SET_P) { + merged->debuglog_name = parent->debuglog_name; + merged->debuglog_fd = parent->debuglog_fd; + } else { + merged->debuglog_name = child->debuglog_name; + merged->debuglog_fd = child->debuglog_fd; + } + + merged->debuglog_level = (child->debuglog_level == NOT_SET + ? parent->debuglog_level : child->debuglog_level); + + merged->cookie_format = (child->cookie_format == NOT_SET + ? parent->cookie_format : child->cookie_format); + merged->argument_separator = (child->argument_separator == NOT_SET + ? parent->argument_separator : child->argument_separator); + + + /* rule inheritance */ + if ((child->rule_inheritance == NOT_SET)||(child->rule_inheritance == 1)) { + merged->rule_inheritance = parent->rule_inheritance; + if ((child->ruleset == NULL)&&(parent->ruleset == NULL)) { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "No rules in this context."); + #endif + + /* Do nothing, there are no rules in either context. */ + } else + if (child->ruleset == NULL) { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using parent rules in this context."); + #endif + + /* Copy the rules from the parent context. */ + merged->ruleset = msre_ruleset_create(parent->ruleset->engine, mp); + copy_rules(mp, parent->ruleset, merged->ruleset, child->rule_exceptions); + } else + if (parent->ruleset == NULL) { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using child rules in this context."); + #endif + + /* Copy child rules. */ + merged->ruleset = msre_ruleset_create(child->ruleset->engine, mp); + merged->ruleset->phase_request_headers = apr_array_copy(mp, + child->ruleset->phase_request_headers); + merged->ruleset->phase_request_body = apr_array_copy(mp, + child->ruleset->phase_request_body); + merged->ruleset->phase_response_headers = apr_array_copy(mp, + child->ruleset->phase_response_headers); + merged->ruleset->phase_response_body = apr_array_copy(mp, + child->ruleset->phase_response_body); + merged->ruleset->phase_logging = apr_array_copy(mp, + child->ruleset->phase_logging); + } else { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using parent then child rules in this context."); + #endif + + /* Copy parent rules, then add child rules to it. */ + merged->ruleset = msre_ruleset_create(parent->ruleset->engine, mp); + copy_rules(mp, parent->ruleset, merged->ruleset, child->rule_exceptions); + + apr_array_cat(merged->ruleset->phase_request_headers, + child->ruleset->phase_request_headers); + apr_array_cat(merged->ruleset->phase_request_body, + child->ruleset->phase_request_body); + apr_array_cat(merged->ruleset->phase_response_headers, + child->ruleset->phase_response_headers); + apr_array_cat(merged->ruleset->phase_response_body, + child->ruleset->phase_response_body); + apr_array_cat(merged->ruleset->phase_logging, + child->ruleset->phase_logging); + } + } else { + merged->rule_inheritance = 0; + if (child->ruleset != NULL) { + /* Copy child rules. */ + merged->ruleset = msre_ruleset_create(child->ruleset->engine, mp); + merged->ruleset->phase_request_headers = apr_array_copy(mp, + child->ruleset->phase_request_headers); + merged->ruleset->phase_request_body = apr_array_copy(mp, + child->ruleset->phase_request_body); + merged->ruleset->phase_response_headers = apr_array_copy(mp, + child->ruleset->phase_response_headers); + merged->ruleset->phase_response_body = apr_array_copy(mp, + child->ruleset->phase_response_body); + merged->ruleset->phase_logging = apr_array_copy(mp, + child->ruleset->phase_logging); + } + } + + /* Merge rule exceptions. */ + merged->rule_exceptions = apr_array_append(mp, parent->rule_exceptions, + child->rule_exceptions); + + /* audit log variables */ + merged->auditlog_flag = (child->auditlog_flag == NOT_SET + ? parent->auditlog_flag : child->auditlog_flag); + merged->auditlog_type = (child->auditlog_type == NOT_SET + ? parent->auditlog_type : child->auditlog_type); + merged->auditlog_dirperms = (child->auditlog_dirperms == NOT_SET + ? parent->auditlog_dirperms : child->auditlog_dirperms); + merged->auditlog_fileperms = (child->auditlog_fileperms == NOT_SET + ? parent->auditlog_fileperms : child->auditlog_fileperms); + if (child->auditlog_fd != NOT_SET_P) { + merged->auditlog_fd = child->auditlog_fd; + merged->auditlog_name = child->auditlog_name; + } else { + merged->auditlog_fd = parent->auditlog_fd; + merged->auditlog_name = parent->auditlog_name; + } + if (child->auditlog2_fd != NOT_SET_P) { + merged->auditlog2_fd = child->auditlog2_fd; + merged->auditlog2_name = child->auditlog2_name; + } else { + merged->auditlog2_fd = parent->auditlog2_fd; + merged->auditlog2_name = parent->auditlog2_name; + } + merged->auditlog_storage_dir = (child->auditlog_storage_dir == NOT_SET_P + ? parent->auditlog_storage_dir : child->auditlog_storage_dir); + merged->auditlog_parts = (child->auditlog_parts == NOT_SET_P + ? parent->auditlog_parts : child->auditlog_parts); + merged->auditlog_relevant_regex = (child->auditlog_relevant_regex == NOT_SET_P + ? parent->auditlog_relevant_regex : child->auditlog_relevant_regex); + + /* Upload */ + merged->tmp_dir = (child->tmp_dir == NOT_SET_P + ? parent->tmp_dir : child->tmp_dir); + merged->upload_dir = (child->upload_dir == NOT_SET_P + ? parent->upload_dir : child->upload_dir); + merged->upload_keep_files = (child->upload_keep_files == NOT_SET + ? parent->upload_keep_files : child->upload_keep_files); + merged->upload_validates_files = (child->upload_validates_files == NOT_SET + ? parent->upload_validates_files : child->upload_validates_files); + merged->upload_filemode = (child->upload_filemode == NOT_SET + ? parent->upload_filemode : child->upload_filemode); + merged->upload_file_limit = (child->upload_file_limit == NOT_SET + ? parent->upload_file_limit : child->upload_file_limit); + + /* Misc */ + merged->data_dir = (child->data_dir == NOT_SET_P + ? parent->data_dir : child->data_dir); + merged->webappid = (child->webappid == NOT_SET_P + ? parent->webappid : child->webappid); + + /* Content injection. */ + merged->content_injection_enabled = (child->content_injection_enabled == NOT_SET + ? parent->content_injection_enabled : child->content_injection_enabled); + + /* PDF XSS protection. */ + merged->pdfp_enabled = (child->pdfp_enabled == NOT_SET + ? parent->pdfp_enabled : child->pdfp_enabled); + merged->pdfp_secret = (child->pdfp_secret == NOT_SET_P + ? parent->pdfp_secret : child->pdfp_secret); + merged->pdfp_timeout = (child->pdfp_timeout == NOT_SET + ? parent->pdfp_timeout : child->pdfp_timeout); + merged->pdfp_token_name = (child->pdfp_token_name == NOT_SET_P + ? parent->pdfp_token_name : child->pdfp_token_name); + merged->pdfp_only_get = (child->pdfp_only_get == NOT_SET + ? parent->pdfp_only_get : child->pdfp_only_get); + merged->pdfp_method = (child->pdfp_method == NOT_SET + ? parent->pdfp_method : child->pdfp_method); + + /* Geo Lookup */ + merged->geo = (child->geo == NOT_SET_P + ? parent->geo : child->geo); + + /* Cache */ + merged->cache_trans = (child->cache_trans == NOT_SET + ? parent->cache_trans : child->cache_trans); + merged->cache_trans_incremental = (child->cache_trans_incremental == NOT_SET + ? parent->cache_trans_incremental : child->cache_trans_incremental); + merged->cache_trans_min = (child->cache_trans_min == (apr_size_t)NOT_SET + ? parent->cache_trans_min : child->cache_trans_min); + merged->cache_trans_max = (child->cache_trans_max == (apr_size_t)NOT_SET + ? parent->cache_trans_max : child->cache_trans_max); + merged->cache_trans_maxitems = (child->cache_trans_maxitems == (apr_size_t)NOT_SET + ? parent->cache_trans_maxitems : child->cache_trans_maxitems); + + /* Merge component signatures. */ + merged->component_signatures = apr_array_append(mp, parent->component_signatures, + child->component_signatures); + + merged->request_encoding = (child->request_encoding == NOT_SET_P + ? parent->request_encoding : child->request_encoding); + + return merged; +} + +/** + * Initialise directory configuration. This function is *not* meant + * to be called for directory configuration instances created during + * the configuration phase. It can only be called on copies of those + * (created fresh for every transaction). + */ +void init_directory_config(directory_config *dcfg) +{ + if (dcfg == NULL) return; + + if (dcfg->is_enabled == NOT_SET) dcfg->is_enabled = 0; + + if (dcfg->reqbody_access == NOT_SET) dcfg->reqbody_access = 0; + if (dcfg->reqbody_buffering == NOT_SET) dcfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_OFF; + if (dcfg->reqbody_inmemory_limit == NOT_SET) + dcfg->reqbody_inmemory_limit = REQUEST_BODY_DEFAULT_INMEMORY_LIMIT; + if (dcfg->reqbody_limit == NOT_SET) dcfg->reqbody_limit = REQUEST_BODY_DEFAULT_LIMIT; + if (dcfg->reqbody_no_files_limit == NOT_SET) dcfg->reqbody_no_files_limit = REQUEST_BODY_NO_FILES_DEFAULT_LIMIT; + if (dcfg->resbody_access == NOT_SET) dcfg->resbody_access = 0; + if (dcfg->of_limit == NOT_SET) dcfg->of_limit = RESPONSE_BODY_DEFAULT_LIMIT; + if (dcfg->of_limit_action == NOT_SET) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT; + + if (dcfg->of_mime_types == NOT_SET_P) { + dcfg->of_mime_types = apr_table_make(dcfg->mp, 3); + if (dcfg->of_mime_types_cleared != 1) { + apr_table_setn(dcfg->of_mime_types, "text/plain", "1"); + apr_table_setn(dcfg->of_mime_types, "text/html", "1"); + } + } + + if (dcfg->debuglog_fd == NOT_SET_P) dcfg->debuglog_fd = NULL; + if (dcfg->debuglog_name == NOT_SET_P) dcfg->debuglog_name = NULL; + if (dcfg->debuglog_level == NOT_SET) dcfg->debuglog_level = 0; + + if (dcfg->cookie_format == NOT_SET) dcfg->cookie_format = 0; + if (dcfg->argument_separator == NOT_SET) dcfg->argument_separator = '&'; + + if (dcfg->rule_inheritance == NOT_SET) dcfg->rule_inheritance = 1; + + /* audit log variables */ + if (dcfg->auditlog_flag == NOT_SET) dcfg->auditlog_flag = 0; + if (dcfg->auditlog_type == NOT_SET) dcfg->auditlog_type = AUDITLOG_SERIAL; + if (dcfg->auditlog_dirperms == NOT_SET) dcfg->auditlog_dirperms = CREATEMODE_DIR; + if (dcfg->auditlog_fileperms == NOT_SET) dcfg->auditlog_fileperms = CREATEMODE; + if (dcfg->auditlog_fd == NOT_SET_P) dcfg->auditlog_fd = NULL; + if (dcfg->auditlog2_fd == NOT_SET_P) dcfg->auditlog2_fd = NULL; + if (dcfg->auditlog_name == NOT_SET_P) dcfg->auditlog_name = NULL; + if (dcfg->auditlog2_name == NOT_SET_P) dcfg->auditlog2_name = NULL; + if (dcfg->auditlog_storage_dir == NOT_SET_P) dcfg->auditlog_storage_dir = NULL; + if (dcfg->auditlog_parts == NOT_SET_P) dcfg->auditlog_parts = "ABCFHZ"; + if (dcfg->auditlog_relevant_regex == NOT_SET_P) dcfg->auditlog_relevant_regex = NULL; + + /* Upload */ + if (dcfg->tmp_dir == NOT_SET_P) dcfg->tmp_dir = guess_tmp_dir(dcfg->mp); + if (dcfg->upload_dir == NOT_SET_P) dcfg->upload_dir = NULL; + if (dcfg->upload_keep_files == NOT_SET) dcfg->upload_keep_files = KEEP_FILES_OFF; + if (dcfg->upload_validates_files == NOT_SET) dcfg->upload_validates_files = 0; + if (dcfg->upload_filemode == NOT_SET) dcfg->upload_filemode = 0600; + if (dcfg->upload_file_limit == NOT_SET) dcfg->upload_file_limit = 100; + + /* Misc */ + if (dcfg->data_dir == NOT_SET_P) dcfg->data_dir = NULL; + if (dcfg->webappid == NOT_SET_P) dcfg->webappid = "default"; + + /* Content injection. */ + if (dcfg->content_injection_enabled == NOT_SET) dcfg->content_injection_enabled = 0; + + /* PDF XSS protection. */ + if (dcfg->pdfp_enabled == NOT_SET) dcfg->pdfp_enabled = 0; + if (dcfg->pdfp_secret == NOT_SET_P) dcfg->pdfp_secret = NULL; + if (dcfg->pdfp_timeout == NOT_SET) dcfg->pdfp_timeout = 10; + if (dcfg->pdfp_token_name == NOT_SET_P) dcfg->pdfp_token_name = "PDFPTOKEN"; + if (dcfg->pdfp_only_get == NOT_SET) dcfg->pdfp_only_get = 1; + if (dcfg->pdfp_method == NOT_SET) dcfg->pdfp_method = PDF_PROTECT_METHOD_TOKEN_REDIRECTION; + + /* Geo Lookup */ + if (dcfg->geo == NOT_SET_P) dcfg->geo = NULL; + + /* Cache */ + if (dcfg->cache_trans == NOT_SET) dcfg->cache_trans = MODSEC_CACHE_DISABLED; + if (dcfg->cache_trans_incremental == NOT_SET) dcfg->cache_trans_incremental = 0; + if (dcfg->cache_trans_min == (apr_size_t)NOT_SET) dcfg->cache_trans_min = 32; + if (dcfg->cache_trans_max == (apr_size_t)NOT_SET) dcfg->cache_trans_max = 1024; + if (dcfg->cache_trans_maxitems == (apr_size_t)NOT_SET) dcfg->cache_trans_maxitems = 512; + + if (dcfg->request_encoding == NOT_SET_P) dcfg->request_encoding = NULL; + +} + +/** + * + */ +static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type, + const char *p1, const char *p2, const char *p3) +{ + char *my_error_msg = NULL; + msre_rule *rule = NULL; + extern msc_engine *modsecurity; + + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Rule: type=%d p1='%s' p2='%s' p3='%s'", type, p1, p2, p3); + #endif + + /* Create a ruleset if one does not exist. */ + if ((dcfg->ruleset == NULL)||(dcfg->ruleset == NOT_SET_P)) { + dcfg->ruleset = msre_ruleset_create(modsecurity->msre, cmd->pool); + if (dcfg->ruleset == NULL) return FATAL_ERROR; + } + + /* Create the rule now. */ + switch(type) { + #if defined(WITH_LUA) + case RULE_TYPE_LUA : + rule = msre_rule_lua_create(dcfg->ruleset, cmd->directive->filename, + cmd->directive->line_num, p1, p2, &my_error_msg); + break; + #endif + default : + rule = msre_rule_create(dcfg->ruleset, type, cmd->directive->filename, + cmd->directive->line_num, p1, p2, p3, &my_error_msg); + break; + } + + if (rule == NULL) { + return my_error_msg; + } + + /* Create default actionset if one does not already exist. */ + if (dcfg->tmp_default_actionset == NULL) { + dcfg->tmp_default_actionset = msre_actionset_create_default(modsecurity->msre); + if (dcfg->tmp_default_actionset == NULL) return FATAL_ERROR; + } + + /* Check some cases prior to merging so we know where it came from */ + + /* Check syntax for chained rules */ + if ((rule->actionset != NULL) && (dcfg->tmp_chain_starter != NULL)) { + /* Must NOT specify a disruptive action. */ + if (rule->actionset->intercept_action != NOT_SET) { + return apr_psprintf(cmd->pool, "ModSecurity: Disruptive actions can only " + "be specified by chain starter rules."); + } + + /* Must NOT specify a skipafter action. */ + if (rule->actionset->skip_after != NOT_SET_P) { + return apr_psprintf(cmd->pool, "ModSecurity: SkipAfter actions can only " + "be specified by chain starter rules."); + } + + /* Must NOT specify a phase. */ + if (rule->actionset->phase != NOT_SET) { + return apr_psprintf(cmd->pool, "ModSecurity: Execution phases can only be " + "specified by chain starter rules."); + } + + /* Must NOT use metadata actions. */ + /* ENH: loop through to check for tags */ + if ((rule->actionset->id != NOT_SET_P) + ||(rule->actionset->rev != NOT_SET_P) + ||(rule->actionset->msg != NOT_SET_P) + ||(rule->actionset->severity != NOT_SET) + ||(rule->actionset->logdata != NOT_SET_P)) + { + return apr_psprintf(cmd->pool, "ModSecurity: Metadata actions (id, rev, msg, tag, severity, logdata) " + " can only be specified by chain starter rules."); + } + + /* Must NOT use skip. */ + if (rule->actionset->skip_count != NOT_SET) { + return apr_psprintf(cmd->pool, "ModSecurity: The skip action can only be used " + " by chain starter rules. "); + } + } + + /* Merge actions with the parent. + * + * ENH Probably do not want this done fully for chained rules. + */ + rule->actionset = msre_actionset_merge(modsecurity->msre, dcfg->tmp_default_actionset, + rule->actionset, 1); + + /* Keep track of the parent action for "block" */ + rule->actionset->parent_intercept_action_rec = dcfg->tmp_default_actionset->intercept_action_rec; + rule->actionset->parent_intercept_action = dcfg->tmp_default_actionset->intercept_action; + + /* Must NOT specify a disruptive action in logging phase. */ + if ((rule->actionset != NULL) + && (rule->actionset->phase == PHASE_LOGGING) + && (rule->actionset->intercept_action != ACTION_ALLOW) + && (rule->actionset->intercept_action != ACTION_ALLOW_REQUEST) + && (rule->actionset->intercept_action != ACTION_NONE) + ) { + return apr_psprintf(cmd->pool, "ModSecurity: Disruptive actions " + "cannot be specified in the logging phase."); + } + + if (dcfg->tmp_chain_starter != NULL) { + rule->chain_starter = dcfg->tmp_chain_starter; + rule->actionset->phase = rule->chain_starter->actionset->phase; + } + + if (rule->actionset->is_chained != 1) { + /* If this rule is part of the chain but does + * not want more rules to follow in the chain + * then cut it (the chain). + */ + dcfg->tmp_chain_starter = NULL; + } else { + /* On the other hand, if this rule wants other + * rules to follow it, then start a new chain + * if there isn't one already. + */ + if (dcfg->tmp_chain_starter == NULL) { + dcfg->tmp_chain_starter = rule; + } + } + + /* Optimisation */ + if ((rule->op_name != NULL)&&(strcasecmp(rule->op_name, "inspectFile") == 0)) { + dcfg->upload_validates_files = 1; + } + + /* Create skip table if one does not already exist. */ + if (dcfg->tmp_rule_placeholders == NULL) { + dcfg->tmp_rule_placeholders = apr_table_make(cmd->pool, 10); + if (dcfg->tmp_rule_placeholders == NULL) return FATAL_ERROR; + } + + /* Keep track of any rule IDs we need to skip after */ + if (rule->actionset->skip_after != NOT_SET_P) { + char *tmp_id = apr_pstrdup(cmd->pool, rule->actionset->skip_after); + apr_table_setn(dcfg->tmp_rule_placeholders, tmp_id, tmp_id); + + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Watching for skipafter target rule id=\"%s\".", tmp_id); + #endif + + } + + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Adding rule %pp phase=%d id=\"%s\".", rule, rule->actionset->phase, (rule->actionset->id == NOT_SET_P + ? "(none)" : rule->actionset->id)); + #endif + + /* Add rule to the recipe. */ + if (msre_ruleset_rule_add(dcfg->ruleset, rule, rule->actionset->phase) < 0) { + return "Internal Error: Failed to add rule to the ruleset."; + } + + /* Add an additional placeholder if this rule ID is on the list */ + if ((rule->actionset->id != NULL) && apr_table_get(dcfg->tmp_rule_placeholders, rule->actionset->id)) { + msre_rule *phrule = apr_palloc(rule->ruleset->mp, sizeof(msre_rule)); + if (phrule == NULL) { + return FATAL_ERROR; + } + + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Adding placeholder %pp for rule %pp id=\"%s\".", phrule, rule, rule->actionset->id); + #endif + + /* shallow copy of original rule with placeholder marked as target */ + memcpy(phrule, rule, sizeof(msre_rule)); + phrule->placeholder = RULE_PH_SKIPAFTER; + + /* Add placeholder. */ + if (msre_ruleset_rule_add(dcfg->ruleset, phrule, phrule->actionset->phase) < 0) { + return "Internal Error: Failed to add placeholder to the ruleset."; + } + + /* No longer need to search for the ID */ + apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id); + } + + /* Update the unparsed rule */ + rule->unparsed = msre_rule_generate_unparsed(dcfg->ruleset->mp, rule, NULL, NULL, NULL); + + return NULL; +} + +/** + * + */ +static const char *add_marker(cmd_parms *cmd, directory_config *dcfg, + const char *p1, const char *p2, const char *p3) +{ + char *my_error_msg = NULL; + msre_rule *rule = NULL; + extern msc_engine *modsecurity; + int p; + + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Rule: type=%d p1='%s' p2='%s' p3='%s'", RULE_TYPE_MARKER, p1, p2, p3); + #endif + + /* Create a ruleset if one does not exist. */ + if ((dcfg->ruleset == NULL)||(dcfg->ruleset == NOT_SET_P)) { + dcfg->ruleset = msre_ruleset_create(modsecurity->msre, cmd->pool); + if (dcfg->ruleset == NULL) return FATAL_ERROR; + } + + /* Create the rule now. */ + rule = msre_rule_create(dcfg->ruleset, RULE_TYPE_MARKER, cmd->directive->filename, cmd->directive->line_num, p1, p2, p3, &my_error_msg); + if (rule == NULL) { + return my_error_msg; + } + + /* This is a marker */ + rule->placeholder = RULE_PH_MARKER; + + /* Add placeholder to each phase */ + for (p = PHASE_FIRST; p <= PHASE_LAST; p++) { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Adding marker %pp phase=%d id=\"%s\".", rule, p, (rule->actionset->id == NOT_SET_P + ? "(none)" : rule->actionset->id)); + #endif + + if (msre_ruleset_rule_add(dcfg->ruleset, rule, p) < 0) { + return "Internal Error: Failed to add marker to the ruleset."; + } + } + + /* No longer need to search for the ID */ + if (dcfg->tmp_rule_placeholders != NULL) { + apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id); + } + + return NULL; +} + +/** + * + */ +static const char *update_rule_action(cmd_parms *cmd, directory_config *dcfg, + const char *p1, const char *p2) +{ + char *my_error_msg = NULL; + msre_rule *rule = NULL; + msre_actionset *new_actionset = NULL; + msre_ruleset *ruleset = dcfg->ruleset; + extern msc_engine *modsecurity; + + /* Get the ruleset if one exists */ + if ((ruleset == NULL)||(ruleset == NOT_SET_P)) { + return NULL; + } + + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Update rule id=\"%s\" with action \"%s\".", p1, p2); + #endif + + /* Fetch the rule */ + rule = msre_ruleset_fetch_rule(ruleset, p1); + if (rule == NULL) { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Update rule id=\"%s\" with action \"%s\" failed: Rule not found.", p1, p2); + #endif + return NULL; + } + + /* Check the rule actionset */ + /* ENH: Can this happen? */ + if (rule->actionset == NULL) { + return apr_psprintf(cmd->pool, "ModSecurity: Attempt to update action for rule \"%s\" failed: Rule does not have an actionset.", p1); + } + + /* Create a new actionset */ + new_actionset = msre_actionset_create(modsecurity->msre, p2, &my_error_msg); + if (new_actionset == NULL) return FATAL_ERROR; + if (my_error_msg != NULL) return my_error_msg; + + /* Must NOT change an id */ + if ((new_actionset->id != NOT_SET_P) && (rule->actionset->id != NULL) && (strcmp(rule->actionset->id, new_actionset->id) != 0)) { + return apr_psprintf(cmd->pool, "ModSecurity: Rule IDs cannot be updated via SecRuleUpdateActionById."); + } + + /* Must NOT alter the phase */ + if ((new_actionset->phase != NOT_SET) && (rule->actionset->phase != new_actionset->phase)) { + return apr_psprintf(cmd->pool, "ModSecurity: Rule phases cannot be updated via SecRuleUpdateActionById."); + } + + #ifdef DEBUG_CONF + { + char *actions = msre_actionset_generate_action_string(ruleset->mp, rule->actionset); + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Update rule %pp id=\"%s\" old action: \"%s\"", + rule, + (rule->actionset->id == NOT_SET_P ? "(none)" : rule->actionset->id), + actions); + } + #endif + + /* Merge new actions with the rule */ + /* ENH: Will this leak the old actionset? */ + rule->actionset = msre_actionset_merge(modsecurity->msre, rule->actionset, + new_actionset, 1); + msre_actionset_set_defaults(rule->actionset); + + /* Update the unparsed rule */ + rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, NULL, NULL, NULL); + + #ifdef DEBUG_CONF + { + char *actions = msre_actionset_generate_action_string(ruleset->mp, rule->actionset); + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Update rule %pp id=\"%s\" new action: \"%s\"", + rule, + (rule->actionset->id == NOT_SET_P ? "(none)" : rule->actionset->id), + actions); + } + #endif + + return NULL; +} + +/* -- Configuration directives -- */ + +static const char *cmd_action(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_ACTION, SECACTION_TARGETS, SECACTION_ARGS, p1); +} + +static const char *cmd_marker(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + const char *action = apr_pstrcat(dcfg->mp, SECMARKER_BASE_ACTIONS, p1, NULL); + return add_marker(cmd, (directory_config *)_dcfg, SECMARKER_TARGETS, SECMARKER_ARGS, action); +} + +static const char *cmd_argument_separator(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + if (strlen(p1) != 1) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid argument separator: %s", p1); + } + + dcfg->argument_separator = p1[0]; + + return NULL; +} + +static const char *cmd_audit_engine(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + directory_config *dcfg = _dcfg; + + if (strcasecmp(p1, "On") == 0) dcfg->auditlog_flag = AUDITLOG_ON; + else + if (strcasecmp(p1, "Off") == 0) dcfg->auditlog_flag = AUDITLOG_OFF; + else + if (strcasecmp(p1, "RelevantOnly") == 0) dcfg->auditlog_flag = AUDITLOG_RELEVANT; + else + return (const char *)apr_psprintf(cmd->pool, + "ModSecurity: Unrecognised parameter value for SecAuditEngine: %s", p1); + + return NULL; +} + +static const char *cmd_audit_log(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + directory_config *dcfg = _dcfg; + + dcfg->auditlog_name = (char *)p1; + + if (dcfg->auditlog_name[0] == '|') { + const char *pipe_name = dcfg->auditlog_name + 1; + piped_log *pipe_log; + + pipe_log = ap_open_piped_log(cmd->pool, pipe_name); + if (pipe_log == NULL) { + return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the audit log pipe: %s", + pipe_name); + } + dcfg->auditlog_fd = ap_piped_log_write_fd(pipe_log); + } + else { + const char *file_name = ap_server_root_relative(cmd->pool, dcfg->auditlog_name); + apr_status_t rc; + + rc = apr_file_open(&dcfg->auditlog_fd, file_name, + APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY, + CREATEMODE, cmd->pool); + + if (rc != APR_SUCCESS) { + return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the audit log file: %s", + file_name); + } + } + + return NULL; +} + +static const char *cmd_audit_log2(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + directory_config *dcfg = _dcfg; + + if (dcfg->auditlog_name == NOT_SET_P) { + return apr_psprintf(cmd->pool, "ModSecurity: Cannot configure a secondary audit log without a primary defined: %s", p1); + } + + dcfg->auditlog2_name = (char *)p1; + + if (dcfg->auditlog2_name[0] == '|') { + const char *pipe_name = ap_server_root_relative(cmd->pool, dcfg->auditlog2_name + 1); + piped_log *pipe_log; + + pipe_log = ap_open_piped_log(cmd->pool, pipe_name); + if (pipe_log == NULL) { + return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the secondary audit log pipe: %s", + pipe_name); + } + dcfg->auditlog2_fd = ap_piped_log_write_fd(pipe_log); + } + else { + const char *file_name = ap_server_root_relative(cmd->pool, dcfg->auditlog2_name); + apr_status_t rc; + + rc = apr_file_open(&dcfg->auditlog2_fd, file_name, + APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY, + CREATEMODE, cmd->pool); + + if (rc != APR_SUCCESS) { + return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the secondary audit log file: %s", + file_name); + } + } + + return NULL; +} + +static const char *cmd_audit_log_parts(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = _dcfg; + + if (is_valid_parts_specification((char *)p1) != 1) { + return apr_psprintf(cmd->pool, "Invalid parts specification for SecAuditLogParts: %s", p1); + } + + dcfg->auditlog_parts = (char *)p1; + return NULL; +} + +static const char *cmd_audit_log_relevant_status(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = _dcfg; + + dcfg->auditlog_relevant_regex = msc_pregcomp(cmd->pool, p1, PCRE_DOTALL, NULL, NULL); + if (dcfg->auditlog_relevant_regex == NULL) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1); + } + + return NULL; +} + +static const char *cmd_audit_log_type(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = _dcfg; + + if (strcasecmp(p1, "Serial") == 0) dcfg->auditlog_type = AUDITLOG_SERIAL; + else + if (strcasecmp(p1, "Concurrent") == 0) dcfg->auditlog_type = AUDITLOG_CONCURRENT; + else + return (const char *)apr_psprintf(cmd->pool, + "ModSecurity: Unrecognised parameter value for SecAuditLogType: %s", p1); + + return NULL; +} + +static const char *cmd_audit_log_dirmode(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "default") == 0) { + dcfg->auditlog_dirperms = NOT_SET; + } + else { + long int mode = strtol(p1, NULL, 8); /* expects octal mode */ + if ((mode == LONG_MAX)||(mode == LONG_MIN)||(mode <= 0)||(mode > 07777)) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecAuditLogDirMode: %s", p1); + } + + dcfg->auditlog_dirperms = mode2fileperms(mode); + } + + return NULL; +} + +static const char *cmd_audit_log_filemode(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "default") == 0) { + dcfg->auditlog_fileperms = NOT_SET; + } + else { + long int mode = strtol(p1, NULL, 8); /* expects octal mode */ + if ((mode == LONG_MAX)||(mode == LONG_MIN)||(mode <= 0)||(mode > 07777)) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecAuditLogFileMode: %s", p1); + } + + dcfg->auditlog_fileperms = mode2fileperms(mode); + } + + return NULL; +} + +static const char *cmd_audit_log_storage_dir(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = _dcfg; + + dcfg->auditlog_storage_dir = ap_server_root_relative(cmd->pool, p1); + + return NULL; +} + +static const char *cmd_cookie_format(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + if (strcmp(p1, "0") == 0) dcfg->cookie_format = COOKIES_V0; + else + if (strcmp(p1, "1") == 0) dcfg->cookie_format = COOKIES_V1; + else { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid cookie format: %s", p1); + } + + return NULL; +} + +static const char *cmd_chroot_dir(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + char cwd[1025] = ""; + + if (cmd->server->is_virtual) { + return "ModSecurity: SecChrootDir not allowed in VirtualHost"; + } + + chroot_dir = (char *)p1; + + if (getcwd(cwd, 1024) == NULL) { + return "ModSecurity: Failed to get the current working directory"; + } + + if (chdir(chroot_dir) < 0) { + return apr_psprintf(cmd->pool, "ModSecurity: Failed to chdir to %s, errno=%d (%s)", + chroot_dir, errno, strerror(errno)); + } + + if (chdir(cwd) < 0) { + return apr_psprintf(cmd->pool, "ModSecurity: Failed to chdir to %s, errno=%d (%s)", + cwd, errno, strerror(errno)); + } + + return NULL; +} + +/** + * Adds component signature to the list of signatures kept in configuration. + */ +static const char *cmd_component_signature(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + /* ENH Enforce "Name/VersionX.Y.Z (comment)" format. */ + *(char **)apr_array_push(dcfg->component_signatures) = (char *)p1; + + return NULL; +} + +static const char *cmd_content_injection(cmd_parms *cmd, void *_dcfg, int flag) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + dcfg->content_injection_enabled = flag; + return NULL; +} + +static const char *cmd_data_dir(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + if (cmd->server->is_virtual) { + return "ModSecurity: SecDataDir not allowed in VirtualHost."; + } + + dcfg->data_dir = ap_server_root_relative(cmd->pool, p1); + + return NULL; +} + +static const char *cmd_debug_log(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + apr_status_t rc; + + dcfg->debuglog_name = ap_server_root_relative(cmd->pool, p1); + + rc = apr_file_open(&dcfg->debuglog_fd, dcfg->debuglog_name, + APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY, + CREATEMODE, cmd->pool); + + if (rc != APR_SUCCESS) { + return apr_psprintf(cmd->pool, "ModSecurity: Failed to open debug log file: %s", + dcfg->debuglog_name); + } + + return NULL; +} + +static const char *cmd_debug_log_level(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + dcfg->debuglog_level = atoi(p1); + if ((dcfg->debuglog_level >= 0)&&(dcfg->debuglog_level <= 9)) return NULL; + + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecDebugLogLevel: %s", p1); +} + +static const char *cmd_default_action(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + extern msc_engine *modsecurity; + char *my_error_msg = NULL; + + dcfg->tmp_default_actionset = msre_actionset_create(modsecurity->msre, p1, &my_error_msg); + if (dcfg->tmp_default_actionset == NULL) { + if (my_error_msg != NULL) return my_error_msg; + else return FATAL_ERROR; + } + + /* Must specify a disruptive action. */ + /* ENH: Remove this requirement? */ + if (dcfg->tmp_default_actionset->intercept_action == NOT_SET) { + return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must specify a disruptive action."); + } + + /* Must specify a phase. */ + /* ENH: Remove this requirement? */ + if (dcfg->tmp_default_actionset->phase == NOT_SET) { + return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must specify a phase."); + } + + /* Must not use metadata actions. */ + /* ENH: loop through to check for tags */ + if ((dcfg->tmp_default_actionset->id != NOT_SET_P) + ||(dcfg->tmp_default_actionset->rev != NOT_SET_P) + ||(dcfg->tmp_default_actionset->msg != NOT_SET_P)) + { + return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not " + "contain any metadata actions (id, rev, msg, tag, severity, logdata)."); + } + /* These are just a warning for now. */ + if ((dcfg->tmp_default_actionset->severity != NOT_SET) + ||(dcfg->tmp_default_actionset->logdata != NOT_SET_P)) + { + ap_log_perror(APLOG_MARK, + APLOG_STARTUP|APLOG_WARNING|APLOG_NOERRNO, 0, cmd->pool, + "ModSecurity: WARNING Using \"severity\" or \"logdata\" in " + "SecDefaultAction is deprecated (%s:%d).", + cmd->directive->filename, cmd->directive->line_num); + } + + /* Must not use chain. */ + if (dcfg->tmp_default_actionset->is_chained != NOT_SET) { + return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not " + "contain a chain action."); + } + + /* Must not use skip. */ + if (dcfg->tmp_default_actionset->skip_count != NOT_SET) { + return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not " + "contain a skip action."); + } + + /* Must not use skipAfter. */ + if (dcfg->tmp_default_actionset->skip_after != NOT_SET_P) { + return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not " + "contain a skipAfter action."); + } + + return NULL; +} + +static const char *cmd_guardian_log(cmd_parms *cmd, void *_dcfg, + const char *p1, const char *p2) +{ + extern char *guardianlog_name; + extern apr_file_t *guardianlog_fd; + extern char *guardianlog_condition; + + if (cmd->server->is_virtual) { + return "ModSecurity: SecGuardianLog not allowed in VirtualHost"; + } + + if (p2 != NULL) { + if (strncmp(p2, "env=", 4) != 0) { + return "ModSecurity: Error in condition clause"; + } + if ( (p2[4] == '\0') || ((p2[4] == '!')&&(p2[5] == '\0')) ) { + return "ModSecurity: Missing variable name"; + } + guardianlog_condition = apr_pstrdup(cmd->pool, p2 + 4); + } + + guardianlog_name = (char *)p1; + + if (guardianlog_name[0] == '|') { + const char *pipe_name = ap_server_root_relative(cmd->pool, guardianlog_name + 1); + piped_log *pipe_log; + + pipe_log = ap_open_piped_log(cmd->pool, pipe_name); + if (pipe_log == NULL) { + return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the guardian log pipe: %s", + pipe_name); + } + guardianlog_fd = ap_piped_log_write_fd(pipe_log); + } + else { + const char *file_name = ap_server_root_relative(cmd->pool, guardianlog_name); + apr_status_t rc; + + rc = apr_file_open(&guardianlog_fd, file_name, + APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY, + CREATEMODE, cmd->pool); + + if (rc != APR_SUCCESS) { + return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the guardian log file: %s", + file_name); + } + } + + return NULL; +} + +/* +* \brief Add SecReadStateLimit configuration option +* +* \param cmd Pointer to configuration data +* \param _dcfg Pointer to directory configuration +* \param p1 Pointer to configuration option +* +* \retval NULL On failure +* \retval apr_psprintf On Success +*/ +static const char *cmd_conn_read_state_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + long int limit; + + if (dcfg == NULL) return NULL; + + limit = strtol(p1, NULL, 10); + if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecReadStateLimit: %s", p1); + } + + conn_read_state_limit = limit; + + return NULL; +} + +static const char *cmd_request_body_inmemory_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + long int limit; + + if (dcfg == NULL) return NULL; + + limit = strtol(p1, NULL, 10); + if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyInMemoryLimit: %s", p1); + } + + dcfg->reqbody_inmemory_limit = limit; + + return NULL; +} + +static const char *cmd_request_body_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + long int limit; + + if (dcfg == NULL) return NULL; + + limit = strtol(p1, NULL, 10); + if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyLimit: %s", p1); + } + + dcfg->reqbody_limit = limit; + + return NULL; +} + +static const char *cmd_request_body_no_files_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + long int limit; + + if (dcfg == NULL) return NULL; + + limit = strtol(p1, NULL, 10); + if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyNoFilesLimit: %s", p1); + } + + dcfg->reqbody_no_files_limit = limit; + + return NULL; +} + +static const char *cmd_request_body_access(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "on") == 0) dcfg->reqbody_access = 1; + else + if (strcasecmp(p1, "off") == 0) dcfg->reqbody_access = 0; + else + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyAccess: %s", p1); + + return NULL; +} + +static const char *cmd_request_encoding(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + /* ENH Validate encoding */ + + dcfg->request_encoding = p1; + + return NULL; +} + +static const char *cmd_response_body_access(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "on") == 0) dcfg->resbody_access = 1; + else + if (strcasecmp(p1, "off") == 0) dcfg->resbody_access = 0; + else + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyAccess: %s", p1); + + return NULL; +} + +static const char *cmd_response_body_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + long int limit; + + limit = strtol(p1, NULL, 10); + if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyLimit: %s", p1); + } + + if (limit > RESPONSE_BODY_HARD_LIMIT) { + return apr_psprintf(cmd->pool, "ModSecurity: Response size limit can not exceed the hard limit: %li", RESPONSE_BODY_HARD_LIMIT); + } + + dcfg->of_limit = limit; + + return NULL; +} + +static const char *cmd_response_body_limit_action(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "ProcessPartial") == 0) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_PARTIAL; + else + if (strcasecmp(p1, "Reject") == 0) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT; + else + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyLimitAction: %s", p1); + + return NULL; +} + +static const char *cmd_response_body_mime_type(cmd_parms *cmd, void *_dcfg, + const char *_p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + char *p1 = apr_pstrdup(cmd->pool, _p1); + + /* TODO check whether the parameter is a valid MIME type of "???" */ + + if ((dcfg->of_mime_types == NULL)||(dcfg->of_mime_types == NOT_SET_P)) { + dcfg->of_mime_types = apr_table_make(cmd->pool, 10); + } + + strtolower_inplace((unsigned char *)p1); + apr_table_setn(dcfg->of_mime_types, p1, "1"); + + return NULL; +} + +static const char *cmd_response_body_mime_types_clear(cmd_parms *cmd, + void *_dcfg) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + dcfg->of_mime_types_cleared = 1; + + if ((dcfg->of_mime_types != NULL)&&(dcfg->of_mime_types != NOT_SET_P)) { + apr_table_clear(dcfg->of_mime_types); + } + + return NULL; +} + +static const char *cmd_rule(cmd_parms *cmd, void *_dcfg, + const char *p1, const char *p2, const char *p3) +{ + return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_NORMAL, p1, p2, p3); +} + +static const char *cmd_rule_engine(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "on") == 0) dcfg->is_enabled = MODSEC_ENABLED; + else + if (strcasecmp(p1, "off") == 0) dcfg->is_enabled = MODSEC_DISABLED; + else + if (strcasecmp(p1, "detectiononly") == 0) dcfg->is_enabled = MODSEC_DETECTION_ONLY; + else + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRuleEngine: %s", p1); + + return NULL; +} + +static const char *cmd_rule_inheritance(cmd_parms *cmd, void *_dcfg, int flag) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + dcfg->rule_inheritance = flag; + return NULL; +} + +static const char *cmd_rule_script(cmd_parms *cmd, void *_dcfg, + const char *p1, const char *p2) +{ + #if defined(WITH_LUA) + const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); + return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_LUA, filename, p2, NULL); + #else + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Ignoring SecRuleScript \"%s\" directive (%s:%d): No Lua scripting support.", p1, cmd->directive->filename, cmd->directive->line_num); + return NULL; + #endif +} + +static const char *cmd_rule_remove_by_id(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); + if (dcfg == NULL) return NULL; + + re->type = RULE_EXCEPTION_REMOVE_ID; + re->param = p1; + *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; + + /* Remove the corresponding rules from the context straight away. */ + msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re); + + return NULL; +} + +static const char *cmd_rule_remove_by_msg(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); + if (dcfg == NULL) return NULL; + + re->type = RULE_EXCEPTION_REMOVE_MSG; + re->param = p1; + re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL); + if (re->param_data == NULL) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1); + } + *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; + + /* Remove the corresponding rules from the context straight away. */ + msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re); + + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Added exception %pp (%d %s) to dcfg %pp.", re, re->type, re->param, dcfg); + #endif + + return NULL; +} + +static const char *cmd_rule_update_action_by_id(cmd_parms *cmd, void *_dcfg, + const char *p1, const char *p2) +{ + return update_rule_action(cmd, (directory_config *)_dcfg, p1, p2); +} + +static const char *cmd_server_signature(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + if (cmd->server->is_virtual) { + return "ModSecurity: SecServerSignature not allowed in VirtualHost"; + } + new_server_signature = (char *)p1; + return NULL; +} + +static const char *cmd_tmp_dir(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "none") == 0) dcfg->tmp_dir = NULL; + else dcfg->tmp_dir = ap_server_root_relative(cmd->pool, p1); + + return NULL; +} + +static const char *cmd_upload_dir(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "none") == 0) dcfg->upload_dir = NULL; + else dcfg->upload_dir = ap_server_root_relative(cmd->pool, p1); + + return NULL; +} + +static const char *cmd_upload_file_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "default") == 0) { + dcfg->upload_file_limit = NOT_SET; + } + else { + dcfg->upload_file_limit = atoi(p1); + } + + return NULL; +} + +static const char *cmd_upload_filemode(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "default") == 0) { + dcfg->upload_filemode = NOT_SET; + } + else { + long int mode = strtol(p1, NULL, 8); /* expects octal mode */ + if ((mode == LONG_MAX)||(mode == LONG_MIN)||(mode <= 0)||(mode > 07777)) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecUploadFileMode: %s", p1); + } + + dcfg->upload_filemode = (int)mode; + } + + return NULL; +} + +static const char *cmd_upload_keep_files(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "on") == 0) { + dcfg->upload_keep_files = KEEP_FILES_ON; + } else + if (strcasecmp(p1, "off") == 0) { + dcfg->upload_keep_files = KEEP_FILES_OFF; + } else + if (strcasecmp(p1, "relevantonly") == 0) { + dcfg->upload_keep_files = KEEP_FILES_RELEVANT_ONLY; + } else { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for SecUploadKeepFiles: %s", + p1); + } + return NULL; +} + +static const char *cmd_web_app_id(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + /* ENH enforce format (letters, digits, ., _, -) */ + dcfg->webappid = p1; + + return NULL; +} + + +/* PCRE Limits */ + +static const char *cmd_pcre_match_limit(cmd_parms *cmd, + void *_dcfg, const char *p1) +{ + long val; + + if (cmd->server->is_virtual) { + return "ModSecurity: SecPcreMatchLimit not allowed in VirtualHost"; + } + + val = atol(p1); + if (val <= 0) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for " + "SecPcreMatchLimit: %s", p1); + } + msc_pcre_match_limit = (unsigned long int)val; + + return NULL; +} + +static const char *cmd_pcre_match_limit_recursion(cmd_parms *cmd, + void *_dcfg, const char *p1) +{ + long val; + + if (cmd->server->is_virtual) { + return "ModSecurity: SecPcreMatchLimitRecursion not allowed in VirtualHost"; + } + + val = atol(p1); + if (val <= 0) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for " + "SecPcreMatchLimitRecursion: %s", p1); + } + msc_pcre_match_limit_recursion = (unsigned long int)val; + + return NULL; +} + + + +/* -- PDF Protection configuration -- */ + +static const char *cmd_pdf_protect(cmd_parms *cmd, void *_dcfg, int flag) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + dcfg->pdfp_enabled = flag; + + return NULL; +} + +static const char *cmd_pdf_protect_secret(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + dcfg->pdfp_secret = p1; + + return NULL; +} + +static const char *cmd_pdf_protect_timeout(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + dcfg->pdfp_timeout = atoi(p1); + + return NULL; +} + +static const char *cmd_pdf_protect_token_name(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + dcfg->pdfp_token_name = p1; + + return NULL; +} + +static const char *cmd_pdf_protect_intercept_get_only(cmd_parms *cmd, + void *_dcfg, int flag) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + dcfg->pdfp_only_get = flag; + + return NULL; +} + +static const char *cmd_pdf_protect_method(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "TokenRedirection") == 0) { + dcfg->pdfp_method = PDF_PROTECT_METHOD_TOKEN_REDIRECTION; + } else + if (strcasecmp(p1, "ForcedDownload") == 0) { + dcfg->pdfp_method = PDF_PROTECT_METHOD_FORCED_DOWNLOAD; + } else { + return (const char *)apr_psprintf(cmd->pool, + "ModSecurity: Unrecognised parameter value for SecPdfProtectMethod: %s", p1); + } + + return NULL; +} + +/* -- Geo Lookup configuration -- */ + +static const char *cmd_geo_lookup_db(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); + char *error_msg; + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + if (geo_init(dcfg, filename, &error_msg) <= 0) { + return error_msg; + } + + return NULL; +} + + +/* -- Cache -- */ + +static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, + const char *p1, const char *p2) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "on") == 0) + dcfg->cache_trans = MODSEC_CACHE_ENABLED; + else if (strcasecmp(p1, "off") == 0) + dcfg->cache_trans = MODSEC_CACHE_DISABLED; + else + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecCacheTransformations: %s", p1); + + /* Process options */ + if (p2 != NULL) { + apr_table_t *vartable = apr_table_make(cmd->pool, 4); + apr_status_t rc; + char *error_msg = NULL; + const char *charval = NULL; + apr_int64_t intval = 0; + + if (vartable == NULL) { + return apr_psprintf(cmd->pool, "ModSecurity: Unable to process options for SecCacheTransformations"); + } + rc = msre_parse_generic(cmd->pool, p2, vartable, &error_msg); + if (rc < 0) { + return apr_psprintf(cmd->pool, "ModSecurity: Unable to parse options for SecCacheTransformations: %s", error_msg); + } + + /* incremental */ + charval = apr_table_get(vartable, "incremental"); + if (charval != NULL) { + if (strcasecmp(charval, "on") == 0) + dcfg->cache_trans_incremental = 1; + else if (strcasecmp(charval, "off") == 0) + dcfg->cache_trans_incremental = 0; + else + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations invalid incremental value: %s", charval); + } + + /* minlen */ + charval = apr_table_get(vartable, "minlen"); + if (charval != NULL) { + intval = apr_atoi64(charval); + if (errno == ERANGE) { + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen out of range: %s", charval); + } + if (intval < 0) { + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be positive: %s", charval); + } + + /* The NOT_SET indicator is -1, a signed long, and therfore + * we cannot be >= the unsigned value of NOT_SET. + */ + if ((unsigned long)intval >= (unsigned long)NOT_SET) { + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be less than: %lu", (unsigned long)NOT_SET); + } + dcfg->cache_trans_min = (apr_size_t)intval; + } + + /* maxlen */ + charval = apr_table_get(vartable, "maxlen"); + if (charval != NULL) { + intval = apr_atoi64(charval); + if (errno == ERANGE) { + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen out of range: %s", charval); + } + if (intval < 0) { + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be positive: %s", charval); + } + + /* The NOT_SET indicator is -1, a signed long, and therfore + * we cannot be >= the unsigned value of NOT_SET. + */ + if ((unsigned long)intval >= (unsigned long)NOT_SET) { + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be less than: %lu", (unsigned long)NOT_SET); + } + if ((intval != 0) && ((apr_size_t)intval < dcfg->cache_trans_min)) { + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must not be less than minlen: %lu < %" APR_SIZE_T_FMT, (unsigned long)intval, dcfg->cache_trans_min); + } + dcfg->cache_trans_max = (apr_size_t)intval; + + } + + /* maxitems */ + charval = apr_table_get(vartable, "maxitems"); + if (charval != NULL) { + intval = apr_atoi64(charval); + if (errno == ERANGE) { + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxitems out of range: %s", charval); + } + if (intval < 0) { + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxitems must be positive: %s", charval); + } + dcfg->cache_trans_maxitems = (apr_size_t)intval; + } + } + + return NULL; +} + + +/* -- Configuration directives definitions -- */ + +#define CMD_SCOPE_MAIN (RSRC_CONF) +#define CMD_SCOPE_ANY (RSRC_CONF | ACCESS_CONF) + +const command_rec module_directives[] = { + + AP_INIT_TAKE1 ( + "SecAction", + cmd_action, + NULL, + CMD_SCOPE_ANY, + "an action list" + ), + + AP_INIT_TAKE1 ( + "SecArgumentSeparator", + cmd_argument_separator, + NULL, + CMD_SCOPE_MAIN, + "character that will be used as separator when parsing application/x-www-form-urlencoded content." + ), + + AP_INIT_TAKE1 ( + "SecAuditEngine", + cmd_audit_engine, + NULL, + CMD_SCOPE_ANY, + "On, Off or RelevantOnly to determine the level of audit logging" + ), + + AP_INIT_TAKE1 ( + "SecAuditLog", + cmd_audit_log, + NULL, + CMD_SCOPE_ANY, + "filename of the primary audit log file" + ), + + AP_INIT_TAKE1 ( + "SecAuditLog2", + cmd_audit_log2, + NULL, + CMD_SCOPE_ANY, + "filename of the secondary audit log file" + ), + + AP_INIT_TAKE1 ( + "SecAuditLogParts", + cmd_audit_log_parts, + NULL, + CMD_SCOPE_ANY, + "list of audit log parts that go into the log." + ), + + AP_INIT_TAKE1 ( + "SecAuditLogRelevantStatus", + cmd_audit_log_relevant_status, + NULL, + CMD_SCOPE_ANY, + "regular expression that will be used to determine if the response status is relevant for audit logging" + ), + + AP_INIT_TAKE1 ( + "SecAuditLogType", + cmd_audit_log_type, + NULL, + CMD_SCOPE_ANY, + "whether to use the old audit log format (Serial) or new (Concurrent)" + ), + + AP_INIT_TAKE1 ( + "SecAuditLogStorageDir", + cmd_audit_log_storage_dir, + NULL, + CMD_SCOPE_ANY, + "path to the audit log storage area; absolute, or relative to the root of the server" + ), + + AP_INIT_TAKE1 ( + "SecAuditLogDirMode", + cmd_audit_log_dirmode, + NULL, + CMD_SCOPE_ANY, + "octal permissions mode for concurrent audit log directories" + ), + + AP_INIT_TAKE1 ( + "SecAuditLogFileMode", + cmd_audit_log_filemode, + NULL, + CMD_SCOPE_ANY, + "octal permissions mode for concurrent audit log files" + ), + + AP_INIT_TAKE12 ( + "SecCacheTransformations", + cmd_cache_transformations, + NULL, + CMD_SCOPE_ANY, + "whether or not to cache transformations. Defaults to true." + ), + + AP_INIT_TAKE1 ( + "SecChrootDir", + cmd_chroot_dir, + NULL, + CMD_SCOPE_MAIN, + "path of the directory to which server will be chrooted" + ), + + AP_INIT_TAKE1 ( + "SecComponentSignature", + cmd_component_signature, + NULL, + CMD_SCOPE_MAIN, + "component signature to add to ModSecurity signature." + ), + + AP_INIT_FLAG ( + "SecContentInjection", + cmd_content_injection, + NULL, + CMD_SCOPE_ANY, + "On or Off" + ), + + AP_INIT_TAKE1 ( + "SecCookieFormat", + cmd_cookie_format, + NULL, + CMD_SCOPE_ANY, + "version of the Cookie specification to use for parsing. Possible values are 0 and 1." + ), + + AP_INIT_TAKE1 ( + "SecDataDir", + cmd_data_dir, + NULL, + CMD_SCOPE_MAIN, + "path to the persistent data storage area" // TODO + ), + + AP_INIT_TAKE1 ( + "SecDebugLog", + cmd_debug_log, + NULL, + CMD_SCOPE_ANY, + "path to the debug log file" + ), + + AP_INIT_TAKE1 ( + "SecDebugLogLevel", + cmd_debug_log_level, + NULL, + CMD_SCOPE_ANY, + "debug log level, which controls the verbosity of logging." + " Use values from 0 (no logging) to 9 (a *lot* of logging)." + ), + + AP_INIT_TAKE1 ( + "SecDefaultAction", + cmd_default_action, + NULL, + CMD_SCOPE_ANY, + "default action list" + ), + + AP_INIT_TAKE1 ( + "SecGeoLookupDB", + cmd_geo_lookup_db, + NULL, + RSRC_CONF, + "database for geographical lookups module." + ), + + AP_INIT_TAKE12 ( + "SecGuardianLog", + cmd_guardian_log, + NULL, + CMD_SCOPE_MAIN, + "The filename of the filter debugging log file" + ), + + AP_INIT_TAKE1 ( + "SecMarker", + cmd_marker, + NULL, + CMD_SCOPE_ANY, + "marker for a skipAfter target" + ), + + AP_INIT_TAKE1 ( + "SecPcreMatchLimit", + cmd_pcre_match_limit, + NULL, + CMD_SCOPE_MAIN, + "PCRE match limit" + ), + + AP_INIT_TAKE1 ( + "SecPcreMatchLimitRecursion", + cmd_pcre_match_limit_recursion, + NULL, + CMD_SCOPE_MAIN, + "PCRE match limit recursion" + ), + + AP_INIT_FLAG ( + "SecPdfProtect", + cmd_pdf_protect, + NULL, + RSRC_CONF, + "enable PDF protection module." + ), + + AP_INIT_TAKE1 ( + "SecPdfProtectSecret", + cmd_pdf_protect_secret, + NULL, + RSRC_CONF, + "secret that will be used to construct protection tokens." + ), + + AP_INIT_TAKE1 ( + "SecPdfProtectTimeout", + cmd_pdf_protect_timeout, + NULL, + RSRC_CONF, + "duration for which protection tokens will be valid." + ), + + AP_INIT_TAKE1 ( + "SecPdfProtectTokenName", + cmd_pdf_protect_token_name, + NULL, + RSRC_CONF, + "name of the protection token. The name 'PDFTOKEN' is used by default." + ), + + AP_INIT_FLAG ( + "SecPdfProtectInterceptGETOnly", + cmd_pdf_protect_intercept_get_only, + NULL, + RSRC_CONF, + "whether or not to intercept only GET and HEAD requess. Defaults to true." + ), + + AP_INIT_TAKE1 ( + "SecPdfProtectMethod", + cmd_pdf_protect_method, + NULL, + RSRC_CONF, + "protection method to use. Can be 'TokenRedirection' (default) or 'ForcedDownload'" + ), + + AP_INIT_TAKE1 ( + "SecRequestBodyAccess", + cmd_request_body_access, + NULL, + CMD_SCOPE_ANY, + "On or Off" + ), + + AP_INIT_TAKE1 ( + "SecReadStateLimit", + cmd_conn_read_state_limit, + NULL, + CMD_SCOPE_ANY, + "maximum number of threads in READ_BUSY state per ip address" + ), + + AP_INIT_TAKE1 ( + "SecRequestBodyInMemoryLimit", + cmd_request_body_inmemory_limit, + NULL, + CMD_SCOPE_ANY, + "maximum request body size that will be placed in memory (except for POST urlencoded requests)." + ), + + AP_INIT_TAKE1 ( + "SecRequestBodyLimit", + cmd_request_body_limit, + NULL, + CMD_SCOPE_ANY, + "maximum request body size ModSecurity will accept." + ), + + AP_INIT_TAKE1 ( + "SecRequestBodyNoFilesLimit", + cmd_request_body_no_files_limit, + NULL, + CMD_SCOPE_ANY, + "maximum request body size ModSecurity will accept, but excluding the size of uploaded files." + ), + + AP_INIT_TAKE1 ( + "SecRequestEncoding", + cmd_request_encoding, + NULL, + CMD_SCOPE_ANY, + "character encoding used in request." + ), + + AP_INIT_TAKE1 ( + "SecResponseBodyAccess", + cmd_response_body_access, + NULL, + CMD_SCOPE_ANY, + "On or Off" + ), + + AP_INIT_TAKE1 ( + "SecResponseBodyLimit", + cmd_response_body_limit, + NULL, + CMD_SCOPE_ANY, + "byte limit for response body" + ), + + AP_INIT_TAKE1 ( + "SecResponseBodyLimitAction", + cmd_response_body_limit_action, + NULL, + CMD_SCOPE_ANY, + "what happens when the response body limit is reached" + ), + + AP_INIT_ITERATE ( + "SecResponseBodyMimeType", + cmd_response_body_mime_type, + NULL, + CMD_SCOPE_ANY, + "adds given MIME types to the list of types that will be buffered on output" + ), + + AP_INIT_NO_ARGS ( + "SecResponseBodyMimeTypesClear", + cmd_response_body_mime_types_clear, + NULL, + CMD_SCOPE_ANY, + "clears the list of MIME types that will be buffered on output" + ), + + AP_INIT_TAKE23 ( + "SecRule", + cmd_rule, + NULL, + CMD_SCOPE_ANY, + "rule target, operator and optional action list" + ), + + AP_INIT_TAKE1 ( + "SecRuleEngine", + cmd_rule_engine, + NULL, + CMD_SCOPE_ANY, + "On or Off" + ), + + AP_INIT_FLAG ( + "SecRuleInheritance", + cmd_rule_inheritance, + NULL, + CMD_SCOPE_ANY, + "On or Off" + ), + + AP_INIT_TAKE12 ( + "SecRuleScript", + cmd_rule_script, + NULL, + CMD_SCOPE_ANY, + "rule script and optional actionlist" + ), + + AP_INIT_ITERATE ( + "SecRuleRemoveById", + cmd_rule_remove_by_id, + NULL, + CMD_SCOPE_ANY, + "rule ID for removal" + ), + + AP_INIT_ITERATE ( + "SecRuleRemoveByMsg", + cmd_rule_remove_by_msg, + NULL, + CMD_SCOPE_ANY, + "rule message for removal" + ), + + AP_INIT_TAKE2 ( + "SecRuleUpdateActionById", + cmd_rule_update_action_by_id, + NULL, + CMD_SCOPE_ANY, + "updated action list" + ), + + AP_INIT_TAKE1 ( + "SecServerSignature", + cmd_server_signature, + NULL, + CMD_SCOPE_MAIN, + "the new signature of the server" + ), + + AP_INIT_TAKE1 ( + "SecTmpDir", + cmd_tmp_dir, + NULL, + CMD_SCOPE_ANY, + "path to the temporary storage area" + ), + + AP_INIT_TAKE1 ( + "SecUploadDir", + cmd_upload_dir, + NULL, + CMD_SCOPE_ANY, + "path to the file upload area" + ), + + AP_INIT_TAKE1 ( + "SecUploadFileLimit", + cmd_upload_file_limit, + NULL, + CMD_SCOPE_ANY, + "limit the number of uploaded files processed" + ), + + AP_INIT_TAKE1 ( + "SecUploadFileMode", + cmd_upload_filemode, + NULL, + CMD_SCOPE_ANY, + "octal permissions mode for uploaded files" + ), + + AP_INIT_TAKE1 ( + "SecUploadKeepFiles", + cmd_upload_keep_files, + NULL, + CMD_SCOPE_ANY, + "On or Off" + ), + + AP_INIT_TAKE1 ( + "SecWebAppId", + cmd_web_app_id, + NULL, + CMD_SCOPE_ANY, + "id" + ), + + { NULL } +}; diff --git a/2.5.13/2.5.x/apache2/apache2_io.c b/2.5.13/2.5.x/apache2/apache2_io.c new file mode 100644 index 00000000..9dd95f13 --- /dev/null +++ b/2.5.13/2.5.x/apache2/apache2_io.c @@ -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 + +#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); + } +} diff --git a/2.5.13/2.5.x/apache2/apache2_util.c b/2.5.13/2.5.x/apache2/apache2_util.c new file mode 100644 index 00000000..05206921 --- /dev/null +++ b/2.5.13/2.5.x/apache2/apache2_util.c @@ -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; +} diff --git a/2.5.13/2.5.x/apache2/api/README b/2.5.13/2.5.x/apache2/api/README new file mode 100644 index 00000000..addfb759 --- /dev/null +++ b/2.5.13/2.5.x/apache2/api/README @@ -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 -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 -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" + diff --git a/2.5.13/2.5.x/apache2/api/mod_op_strstr.c b/2.5.13/2.5.x/apache2/api/mod_op_strstr.c new file mode 100644 index 00000000..46986e79 --- /dev/null +++ b/2.5.13/2.5.x/apache2/api/mod_op_strstr.c @@ -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; +} diff --git a/2.5.13/2.5.x/apache2/api/mod_tfn_reverse.c b/2.5.13/2.5.x/apache2/api/mod_tfn_reverse.c new file mode 100644 index 00000000..8750678d --- /dev/null +++ b/2.5.13/2.5.x/apache2/api/mod_tfn_reverse.c @@ -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 */ +}; + diff --git a/2.5.13/2.5.x/apache2/api/mod_var_remote_addr_port.c b/2.5.13/2.5.x/apache2/api/mod_var_remote_addr_port.c new file mode 100644 index 00000000..ffd18f1e --- /dev/null +++ b/2.5.13/2.5.x/apache2/api/mod_var_remote_addr_port.c @@ -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 */ +}; + diff --git a/2.5.13/2.5.x/apache2/autogen.sh b/2.5.13/2.5.x/apache2/autogen.sh new file mode 100755 index 00000000..197c2698 --- /dev/null +++ b/2.5.13/2.5.x/apache2/autogen.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +rm -rf autom4te.cache + +#automake --add-missing --copy +autoreconf --install diff --git a/2.5.13/2.5.x/apache2/build/apxs-wrapper.in b/2.5.13/2.5.x/apache2/build/apxs-wrapper.in new file mode 100755 index 00000000..7e032731 --- /dev/null +++ b/2.5.13/2.5.x/apache2/build/apxs-wrapper.in @@ -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 diff --git a/2.5.13/2.5.x/apache2/build/find_apr.m4 b/2.5.13/2.5.x/apache2/build/find_apr.m4 new file mode 100644 index 00000000..6c2435af --- /dev/null +++ b/2.5.13/2.5.x/apache2/build/find_apr.m4 @@ -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 +]) diff --git a/2.5.13/2.5.x/apache2/build/find_apu.m4 b/2.5.13/2.5.x/apache2/build/find_apu.m4 new file mode 100644 index 00000000..27a96a89 --- /dev/null +++ b/2.5.13/2.5.x/apache2/build/find_apu.m4 @@ -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 +]) diff --git a/2.5.13/2.5.x/apache2/build/find_curl.m4 b/2.5.13/2.5.x/apache2/build/find_curl.m4 new file mode 100644 index 00000000..1e1dcb13 --- /dev/null +++ b/2.5.13/2.5.x/apache2/build/find_curl.m4 @@ -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 +]) diff --git a/2.5.13/2.5.x/apache2/build/find_lua.m4 b/2.5.13/2.5.x/apache2/build/find_lua.m4 new file mode 100644 index 00000000..5c995615 --- /dev/null +++ b/2.5.13/2.5.x/apache2/build/find_lua.m4 @@ -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 +]) diff --git a/2.5.13/2.5.x/apache2/build/find_pcre.m4 b/2.5.13/2.5.x/apache2/build/find_pcre.m4 new file mode 100644 index 00000000..9241dbaf --- /dev/null +++ b/2.5.13/2.5.x/apache2/build/find_pcre.m4 @@ -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 +]) diff --git a/2.5.13/2.5.x/apache2/build/find_xml.m4 b/2.5.13/2.5.x/apache2/build/find_xml.m4 new file mode 100644 index 00000000..cd82694f --- /dev/null +++ b/2.5.13/2.5.x/apache2/build/find_xml.m4 @@ -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 +]) diff --git a/2.5.13/2.5.x/apache2/build/install-sh b/2.5.13/2.5.x/apache2/build/install-sh new file mode 100755 index 00000000..b6376ec2 --- /dev/null +++ b/2.5.13/2.5.x/apache2/build/install-sh @@ -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 +## +# +# 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 +## +# +# 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 + diff --git a/2.5.13/2.5.x/apache2/build/libtool.m4 b/2.5.13/2.5.x/apache2/build/libtool.m4 new file mode 100644 index 00000000..e58eac9f --- /dev/null +++ b/2.5.13/2.5.x/apache2/build/libtool.m4 @@ -0,0 +1,7373 @@ +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +m4_define([_LT_COPYING], [dnl +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +]) + +# serial 56 LT_INIT + + +# LT_PREREQ(VERSION) +# ------------------ +# Complain and exit if this libtool version is less that VERSION. +m4_defun([LT_PREREQ], +[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, + [m4_default([$3], + [m4_fatal([Libtool version $1 or higher is required], + 63)])], + [$2])]) + + +# _LT_CHECK_BUILDDIR +# ------------------ +# Complain if the absolute build directory name contains unusual characters +m4_defun([_LT_CHECK_BUILDDIR], +[case `pwd` in + *\ * | *\ *) + AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; +esac +]) + + +# LT_INIT([OPTIONS]) +# ------------------ +AC_DEFUN([LT_INIT], +[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT +AC_BEFORE([$0], [LT_LANG])dnl +AC_BEFORE([$0], [LT_OUTPUT])dnl +AC_BEFORE([$0], [LTDL_INIT])dnl +m4_require([_LT_CHECK_BUILDDIR])dnl + +dnl Autoconf doesn't catch unexpanded LT_ macros by default: +m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl +m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl +dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 +dnl unless we require an AC_DEFUNed macro: +AC_REQUIRE([LTOPTIONS_VERSION])dnl +AC_REQUIRE([LTSUGAR_VERSION])dnl +AC_REQUIRE([LTVERSION_VERSION])dnl +AC_REQUIRE([LTOBSOLETE_VERSION])dnl +m4_require([_LT_PROG_LTMAIN])dnl + +dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(apr_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +_LT_SETUP + +# Only expand once: +m4_define([LT_INIT]) +])# LT_INIT + +# Old names: +AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) +AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PROG_LIBTOOL], []) +dnl AC_DEFUN([AM_PROG_LIBTOOL], []) + + +# _LT_CC_BASENAME(CC) +# ------------------- +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +m4_defun([_LT_CC_BASENAME], +[for cc_temp in $1""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` +]) + + +# _LT_FILEUTILS_DEFAULTS +# ---------------------- +# It is okay to use these file commands and assume they have been set +# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. +m4_defun([_LT_FILEUTILS_DEFAULTS], +[: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} +])# _LT_FILEUTILS_DEFAULTS + + +# _LT_SETUP +# --------- +m4_defun([_LT_SETUP], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +_LT_DECL([], [host_alias], [0], [The host system])dnl +_LT_DECL([], [host], [0])dnl +_LT_DECL([], [host_os], [0])dnl +dnl +_LT_DECL([], [build_alias], [0], [The build system])dnl +_LT_DECL([], [build], [0])dnl +_LT_DECL([], [build_os], [0])dnl +dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +test -z "$LN_S" && LN_S="ln -s" +_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl +dnl +AC_REQUIRE([LT_CMD_MAX_LEN])dnl +_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl +_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl +dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_CMD_RELOAD])dnl +m4_require([_LT_CHECK_MAGIC_METHOD])dnl +m4_require([_LT_CMD_OLD_ARCHIVE])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl + +_LT_CONFIG_LIBTOOL_INIT([ +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi +]) +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +_LT_CHECK_OBJDIR + +m4_require([_LT_TAG_COMPILER])dnl +_LT_PROG_ECHO_BACKSLASH + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([["`\\]]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld="$lt_cv_prog_gnu_ld" + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +_LT_CC_BASENAME([$compiler]) + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + _LT_PATH_MAGIC + fi + ;; +esac + +# Use C for the default configuration in the libtool script +LT_SUPPORTED_TAG([CC]) +_LT_LANG_C_CONFIG +_LT_LANG_DEFAULT_CONFIG +_LT_CONFIG_COMMANDS +])# _LT_SETUP + + +# _LT_PROG_LTMAIN +# --------------- +# Note that this code is called both from `configure', and `config.status' +# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, +# `config.status' has no value for ac_aux_dir unless we are using Automake, +# so we pass a copy along to make sure it has a sensible value anyway. +m4_defun([_LT_PROG_LTMAIN], +[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl +_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) +ltmain="$ac_aux_dir/ltmain.sh" +])# _LT_PROG_LTMAIN + + +## ------------------------------------- ## +## Accumulate code for creating libtool. ## +## ------------------------------------- ## + +# So that we can recreate a full libtool script including additional +# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS +# in macros and then make a single call at the end using the `libtool' +# label. + + +# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) +# ---------------------------------------- +# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL_INIT], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_INIT], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_INIT]) + + +# _LT_CONFIG_LIBTOOL([COMMANDS]) +# ------------------------------ +# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) + + +# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) +# ----------------------------------------------------- +m4_defun([_LT_CONFIG_SAVE_COMMANDS], +[_LT_CONFIG_LIBTOOL([$1]) +_LT_CONFIG_LIBTOOL_INIT([$2]) +]) + + +# _LT_FORMAT_COMMENT([COMMENT]) +# ----------------------------- +# Add leading comment marks to the start of each line, and a trailing +# full-stop to the whole comment if one is not present already. +m4_define([_LT_FORMAT_COMMENT], +[m4_ifval([$1], [ +m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], + [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) +)]) + + + +## ------------------------ ## +## FIXME: Eliminate VARNAME ## +## ------------------------ ## + + +# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) +# ------------------------------------------------------------------- +# CONFIGNAME is the name given to the value in the libtool script. +# VARNAME is the (base) name used in the configure script. +# VALUE may be 0, 1 or 2 for a computed quote escaped value based on +# VARNAME. Any other value will be used directly. +m4_define([_LT_DECL], +[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], + [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], + [m4_ifval([$1], [$1], [$2])]) + lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) + m4_ifval([$4], + [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) + lt_dict_add_subkey([lt_decl_dict], [$2], + [tagged?], [m4_ifval([$5], [yes], [no])])]) +]) + + +# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) +# -------------------------------------------------------- +m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) + + +# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_tag_varnames], +[_lt_decl_filter([tagged?], [yes], $@)]) + + +# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) +# --------------------------------------------------------- +m4_define([_lt_decl_filter], +[m4_case([$#], + [0], [m4_fatal([$0: too few arguments: $#])], + [1], [m4_fatal([$0: too few arguments: $#: $1])], + [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], + [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], + [lt_dict_filter([lt_decl_dict], $@)])[]dnl +]) + + +# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) +# -------------------------------------------------- +m4_define([lt_decl_quote_varnames], +[_lt_decl_filter([value], [1], $@)]) + + +# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_dquote_varnames], +[_lt_decl_filter([value], [2], $@)]) + + +# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_varnames_tagged], +[m4_assert([$# <= 2])dnl +_$0(m4_quote(m4_default([$1], [[, ]])), + m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), + m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) +m4_define([_lt_decl_varnames_tagged], +[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) + + +# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_all_varnames], +[_$0(m4_quote(m4_default([$1], [[, ]])), + m4_if([$2], [], + m4_quote(lt_decl_varnames), + m4_quote(m4_shift($@))))[]dnl +]) +m4_define([_lt_decl_all_varnames], +[lt_join($@, lt_decl_varnames_tagged([$1], + lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl +]) + + +# _LT_CONFIG_STATUS_DECLARE([VARNAME]) +# ------------------------------------ +# Quote a variable value, and forward it to `config.status' so that its +# declaration there will have the same value as in `configure'. VARNAME +# must have a single quote delimited value for this to work. +m4_define([_LT_CONFIG_STATUS_DECLARE], +[$1='`$ECHO "X$][$1" | $Xsed -e "$delay_single_quote_subst"`']) + + +# _LT_CONFIG_STATUS_DECLARATIONS +# ------------------------------ +# We delimit libtool config variables with single quotes, so when +# we write them to config.status, we have to be sure to quote all +# embedded single quotes properly. In configure, this macro expands +# each variable declared with _LT_DECL (and _LT_TAGDECL) into: +# +# ='`$ECHO "X$" | $Xsed -e "$delay_single_quote_subst"`' +m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], +[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), + [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAGS +# ---------------- +# Output comment and list of tags supported by the script +m4_defun([_LT_LIBTOOL_TAGS], +[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl +available_tags="_LT_TAGS"dnl +]) + + +# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) +# ----------------------------------- +# Extract the dictionary values for VARNAME (optionally with TAG) and +# expand to a commented shell variable setting: +# +# # Some comment about what VAR is for. +# visible_name=$lt_internal_name +m4_define([_LT_LIBTOOL_DECLARE], +[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], + [description])))[]dnl +m4_pushdef([_libtool_name], + m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl +m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), + [0], [_libtool_name=[$]$1], + [1], [_libtool_name=$lt_[]$1], + [2], [_libtool_name=$lt_[]$1], + [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl +m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl +]) + + +# _LT_LIBTOOL_CONFIG_VARS +# ----------------------- +# Produce commented declarations of non-tagged libtool config variables +# suitable for insertion in the LIBTOOL CONFIG section of the `libtool' +# script. Tagged libtool config variables (even for the LIBTOOL CONFIG +# section) are produced by _LT_LIBTOOL_TAG_VARS. +m4_defun([_LT_LIBTOOL_CONFIG_VARS], +[m4_foreach([_lt_var], + m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAG_VARS(TAG) +# ------------------------- +m4_define([_LT_LIBTOOL_TAG_VARS], +[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) + + +# _LT_TAGVAR(VARNAME, [TAGNAME]) +# ------------------------------ +m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) + + +# _LT_CONFIG_COMMANDS +# ------------------- +# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of +# variables for single and double quote escaping we saved from calls +# to _LT_DECL, we can put quote escaped variables declarations +# into `config.status', and then the shell code to quote escape them in +# for loops in `config.status'. Finally, any additional code accumulated +# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. +m4_defun([_LT_CONFIG_COMMANDS], +[AC_PROVIDE_IFELSE([LT_OUTPUT], + dnl If the libtool generation code has been placed in $CONFIG_LT, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $CONFIG_LT later, so it + dnl needs to know what name is stored there: + [AC_CONFIG_COMMANDS([libtool], + [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], + dnl If the libtool generation code is destined for config.status, + dnl expand the accumulated commands and init code now: + [AC_CONFIG_COMMANDS([libtool], + [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) +])#_LT_CONFIG_COMMANDS + + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], +[ + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +_LT_CONFIG_STATUS_DECLARATIONS +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# Quote evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_quote_varnames); do + case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_dquote_varnames); do + case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Fix-up fallback echo if it was mangled by the above quoting rules. +case \$lt_ECHO in +*'\\\[$]0 --fallback-echo"')dnl " + lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\[$]0 --fallback-echo"\[$]/\[$]0 --fallback-echo"/'\` + ;; +esac + +_LT_OUTPUT_LIBTOOL_INIT +]) + + +# LT_OUTPUT +# --------- +# This macro allows early generation of the libtool script (before +# AC_OUTPUT is called), incase it is used in configure for compilation +# tests. +AC_DEFUN([LT_OUTPUT], +[: ${CONFIG_LT=./config.lt} +AC_MSG_NOTICE([creating $CONFIG_LT]) +cat >"$CONFIG_LT" <<_LTEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate a libtool stub with the current configuration. + +lt_cl_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AS_SHELL_SANITIZE +_AS_PREPARE + +exec AS_MESSAGE_FD>&1 +exec AS_MESSAGE_LOG_FD>>config.log +{ + echo + AS_BOX([Running $as_me.]) +} >&AS_MESSAGE_LOG_FD + +lt_cl_help="\ +\`$as_me' creates a local libtool stub from the current configuration, +for use in further configure time tests before the real libtool is +generated. + +Usage: $[0] [[OPTIONS]] + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + +Report bugs to ." + +lt_cl_version="\ +m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl +m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) +configured by $[0], generated by m4_PACKAGE_STRING. + +Copyright (C) 2008 Free Software Foundation, Inc. +This config.lt script is free software; the Free Software Foundation +gives unlimited permision to copy, distribute and modify it." + +while test $[#] != 0 +do + case $[1] in + --version | --v* | -V ) + echo "$lt_cl_version"; exit 0 ;; + --help | --h* | -h ) + echo "$lt_cl_help"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --quiet | --q* | --silent | --s* | -q ) + lt_cl_silent=: ;; + + -*) AC_MSG_ERROR([unrecognized option: $[1] +Try \`$[0] --help' for more information.]) ;; + + *) AC_MSG_ERROR([unrecognized argument: $[1] +Try \`$[0] --help' for more information.]) ;; + esac + shift +done + +if $lt_cl_silent; then + exec AS_MESSAGE_FD>/dev/null +fi +_LTEOF + +cat >>"$CONFIG_LT" <<_LTEOF +_LT_OUTPUT_LIBTOOL_COMMANDS_INIT +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AC_MSG_NOTICE([creating $ofile]) +_LT_OUTPUT_LIBTOOL_COMMANDS +AS_EXIT(0) +_LTEOF +chmod +x "$CONFIG_LT" + +# configure is writing to config.log, but config.lt does its own redirection, +# appending to config.log, which fails on DOS, as config.log is still kept +# open by configure. Here we exec the FD to /dev/null, effectively closing +# config.log, so it can be properly (re)opened and appended to by config.lt. +if test "$no_create" != yes; then + lt_cl_success=: + test "$silent" = yes && + lt_config_lt_args="$lt_config_lt_args --quiet" + exec AS_MESSAGE_LOG_FD>/dev/null + $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false + exec AS_MESSAGE_LOG_FD>>config.log + $lt_cl_success || AS_EXIT(1) +fi +])# LT_OUTPUT + + +# _LT_CONFIG(TAG) +# --------------- +# If TAG is the built-in tag, create an initial libtool script with a +# default configuration from the untagged config vars. Otherwise add code +# to config.status for appending the configuration named by TAG from the +# matching tagged config vars. +m4_defun([_LT_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_CONFIG_SAVE_COMMANDS([ + m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl + m4_if(_LT_TAG, [C], [ + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +_LT_COPYING +_LT_LIBTOOL_TAGS + +# ### BEGIN LIBTOOL CONFIG +_LT_LIBTOOL_CONFIG_VARS +_LT_LIBTOOL_TAG_VARS +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + _LT_PROG_LTMAIN + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + _LT_PROG_XSI_SHELLFNS + + sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +], +[cat <<_LT_EOF >> "$ofile" + +dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded +dnl in a comment (ie after a #). +# ### BEGIN LIBTOOL TAG CONFIG: $1 +_LT_LIBTOOL_TAG_VARS(_LT_TAG) +# ### END LIBTOOL TAG CONFIG: $1 +_LT_EOF +])dnl /m4_if +], +[m4_if([$1], [], [ + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile'], []) +])dnl /_LT_CONFIG_SAVE_COMMANDS +])# _LT_CONFIG + + +# LT_SUPPORTED_TAG(TAG) +# --------------------- +# Trace this macro to discover what tags are supported by the libtool +# --tag option, using: +# autoconf --trace 'LT_SUPPORTED_TAG:$1' +AC_DEFUN([LT_SUPPORTED_TAG], []) + + +# C support is built-in for now +m4_define([_LT_LANG_C_enabled], []) +m4_define([_LT_TAGS], []) + + +# LT_LANG(LANG) +# ------------- +# Enable libtool support for the given language if not already enabled. +AC_DEFUN([LT_LANG], +[AC_BEFORE([$0], [LT_OUTPUT])dnl +m4_case([$1], + [C], [_LT_LANG(C)], + [C++], [_LT_LANG(CXX)], + [Java], [_LT_LANG(GCJ)], + [Fortran 77], [_LT_LANG(F77)], + [Fortran], [_LT_LANG(FC)], + [Windows Resource], [_LT_LANG(RC)], + [m4_ifdef([_LT_LANG_]$1[_CONFIG], + [_LT_LANG($1)], + [m4_fatal([$0: unsupported language: "$1"])])])dnl +])# LT_LANG + + +# _LT_LANG(LANGNAME) +# ------------------ +m4_defun([_LT_LANG], +[m4_ifdef([_LT_LANG_]$1[_enabled], [], + [LT_SUPPORTED_TAG([$1])dnl + m4_append([_LT_TAGS], [$1 ])dnl + m4_define([_LT_LANG_]$1[_enabled], [])dnl + _LT_LANG_$1_CONFIG($1)])dnl +])# _LT_LANG + + +# _LT_LANG_DEFAULT_CONFIG +# ----------------------- +m4_defun([_LT_LANG_DEFAULT_CONFIG], +[AC_PROVIDE_IFELSE([AC_PROG_CXX], + [LT_LANG(CXX)], + [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) + +AC_PROVIDE_IFELSE([AC_PROG_F77], + [LT_LANG(F77)], + [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) + +AC_PROVIDE_IFELSE([AC_PROG_FC], + [LT_LANG(FC)], + [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) + +dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal +dnl pulling things in needlessly. +AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([LT_PROG_GCJ], + [LT_LANG(GCJ)], + [m4_ifdef([AC_PROG_GCJ], + [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([A][M_PROG_GCJ], + [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([LT_PROG_GCJ], + [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) + +AC_PROVIDE_IFELSE([LT_PROG_RC], + [LT_LANG(RC)], + [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) +])# _LT_LANG_DEFAULT_CONFIG + +# Obsolete macros: +AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) +AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) +AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) +AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_CXX], []) +dnl AC_DEFUN([AC_LIBTOOL_F77], []) +dnl AC_DEFUN([AC_LIBTOOL_FC], []) +dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) + + +# _LT_TAG_COMPILER +# ---------------- +m4_defun([_LT_TAG_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl +_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl +_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl +_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_TAG_COMPILER + + +# _LT_COMPILER_BOILERPLATE +# ------------------------ +# Check for compiler boilerplate output or warnings with +# the simple compiler test code. +m4_defun([_LT_COMPILER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* +])# _LT_COMPILER_BOILERPLATE + + +# _LT_LINKER_BOILERPLATE +# ---------------------- +# Check for linker boilerplate output or warnings with +# the simple link test code. +m4_defun([_LT_LINKER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* +])# _LT_LINKER_BOILERPLATE + +# _LT_REQUIRED_DARWIN_CHECKS +# ------------------------- +m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ + case $host_os in + rhapsody* | darwin*) + AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) + AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) + AC_CHECK_TOOL([LIPO], [lipo], [:]) + AC_CHECK_TOOL([OTOOL], [otool], [:]) + AC_CHECK_TOOL([OTOOL64], [otool64], [:]) + _LT_DECL([], [DSYMUTIL], [1], + [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) + _LT_DECL([], [NMEDIT], [1], + [Tool to change global to local symbols on Mac OS X]) + _LT_DECL([], [LIPO], [1], + [Tool to manipulate fat objects and archives on Mac OS X]) + _LT_DECL([], [OTOOL], [1], + [ldd/readelf like tool for Mach-O binaries on Mac OS X]) + _LT_DECL([], [OTOOL64], [1], + [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) + + AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], + [lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi]) + AC_CACHE_CHECK([for -exported_symbols_list linker flag], + [lt_cv_ld_exported_symbols_list], + [lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [lt_cv_ld_exported_symbols_list=yes], + [lt_cv_ld_exported_symbols_list=no]) + LDFLAGS="$save_LDFLAGS" + ]) + case $host_os in + rhapsody* | darwin1.[[012]]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[[012]]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac +]) + + +# _LT_DARWIN_LINKER_FEATURES +# -------------------------- +# Checks for linker and compiler features on darwin +m4_defun([_LT_DARWIN_LINKER_FEATURES], +[ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(whole_archive_flag_spec, $1)='' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=echo + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + m4_if([$1], [CXX], +[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi +],[]) + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi +]) + +# _LT_SYS_MODULE_PATH_AIX +# ----------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +m4_defun([_LT_SYS_MODULE_PATH_AIX], +[m4_require([_LT_DECL_SED])dnl +AC_LINK_IFELSE(AC_LANG_PROGRAM,[ +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi],[]) +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi +])# _LT_SYS_MODULE_PATH_AIX + + +# _LT_SHELL_INIT(ARG) +# ------------------- +m4_define([_LT_SHELL_INIT], +[ifdef([AC_DIVERSION_NOTICE], + [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)], + [AC_DIVERT_PUSH(NOTICE)]) +$1 +AC_DIVERT_POP +])# _LT_SHELL_INIT + + +# _LT_PROG_ECHO_BACKSLASH +# ----------------------- +# Add some code to the start of the generated configure script which +# will find an echo command which doesn't interpret backslashes. +m4_defun([_LT_PROG_ECHO_BACKSLASH], +[_LT_SHELL_INIT([ +# Check that we are running under the correct shell. +SHELL=${CONFIG_SHELL-/bin/sh} + +case X$lt_ECHO in +X*--fallback-echo) + # Remove one level of quotation (which was required for Make). + ECHO=`echo "$lt_ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','` + ;; +esac + +ECHO=${lt_ECHO-echo} +if test "X[$]1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X[$]1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then + # Yippee, $ECHO works! + : +else + # Restart under the correct shell. + exec $SHELL "[$]0" --no-reexec ${1+"[$]@"} +fi + +if test "X[$]1" = X--fallback-echo; then + # used as fallback echo + shift + cat <<_LT_EOF +[$]* +_LT_EOF + exit 0 +fi + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +if test -z "$lt_ECHO"; then + if test "X${echo_test_string+set}" != Xset; then + # find a string as large as possible, as long as the shell can cope with it + for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do + # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... + if { echo_test_string=`eval $cmd`; } 2>/dev/null && + { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null + then + break + fi + done + fi + + if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && + echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + : + else + # The Solaris, AIX, and Digital Unix default echo programs unquote + # backslashes. This makes it impossible to quote backslashes using + # echo "$something" | sed 's/\\/\\\\/g' + # + # So, first we look for a working echo in the user's PATH. + + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for dir in $PATH /usr/ucb; do + IFS="$lt_save_ifs" + if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && + test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + ECHO="$dir/echo" + break + fi + done + IFS="$lt_save_ifs" + + if test "X$ECHO" = Xecho; then + # We didn't find a better echo, so look for alternatives. + if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' && + echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # This shell has a builtin print -r that does the trick. + ECHO='print -r' + elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } && + test "X$CONFIG_SHELL" != X/bin/ksh; then + # If we have ksh, try running configure again with it. + ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} + export ORIGINAL_CONFIG_SHELL + CONFIG_SHELL=/bin/ksh + export CONFIG_SHELL + exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"} + else + # Try using printf. + ECHO='printf %s\n' + if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && + echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # Cool, printf works + : + elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL + export CONFIG_SHELL + SHELL="$CONFIG_SHELL" + export SHELL + ECHO="$CONFIG_SHELL [$]0 --fallback-echo" + elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + ECHO="$CONFIG_SHELL [$]0 --fallback-echo" + else + # maybe with a smaller string... + prev=: + + for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do + if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null + then + break + fi + prev="$cmd" + done + + if test "$prev" != 'sed 50q "[$]0"'; then + echo_test_string=`eval $prev` + export echo_test_string + exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"} + else + # Oops. We lost completely, so just stick with echo. + ECHO=echo + fi + fi + fi + fi + fi +fi + +# Copy echo and quote the copy suitably for passing to libtool from +# the Makefile, instead of quoting the original, which is used later. +lt_ECHO=$ECHO +if test "X$lt_ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then + lt_ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo" +fi + +AC_SUBST(lt_ECHO) +]) +_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) +_LT_DECL([], [ECHO], [1], + [An echo program that does not interpret backslashes]) +])# _LT_PROG_ECHO_BACKSLASH + + +# _LT_ENABLE_LOCK +# --------------- +m4_defun([_LT_ENABLE_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AS_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '[#]line __oline__ "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_i386" + ;; + ppc64-*linux*|powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + ppc*-*linux*|powerpc*-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +sparc*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) LD="${LD-ld} -m elf64_sparc" ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks="$enable_libtool_lock" +])# _LT_ENABLE_LOCK + + +# _LT_CMD_OLD_ARCHIVE +# ------------------- +m4_defun([_LT_CMD_OLD_ARCHIVE], +[AC_CHECK_TOOL(AR, ar, false) +test -z "$AR" && AR=ar +test -z "$AR_FLAGS" && AR_FLAGS=cru +_LT_DECL([], [AR], [1], [The archiver]) +_LT_DECL([], [AR_FLAGS], [1]) + +AC_CHECK_TOOL(STRIP, strip, :) +test -z "$STRIP" && STRIP=: +_LT_DECL([], [STRIP], [1], [A symbol stripping program]) + +AC_CHECK_TOOL(RANLIB, ranlib, :) +test -z "$RANLIB" && RANLIB=: +_LT_DECL([], [RANLIB], [1], + [Commands used to install an old-style archive]) + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" +fi +_LT_DECL([], [old_postinstall_cmds], [2]) +_LT_DECL([], [old_postuninstall_cmds], [2]) +_LT_TAGDECL([], [old_archive_cmds], [2], + [Commands used to build an old-style archive]) +])# _LT_CMD_OLD_ARCHIVE + + +# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([_LT_COMPILER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi + $RM conftest* +]) + +if test x"[$]$2" = xyes; then + m4_if([$5], , :, [$5]) +else + m4_if([$6], , :, [$6]) +fi +])# _LT_COMPILER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) + + +# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------- +# Check whether the given linker option works +AC_DEFUN([_LT_LINKER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $3" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + else + $2=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" +]) + +if test x"[$]$2" = xyes; then + m4_if([$4], , :, [$4]) +else + m4_if([$5], , :, [$5]) +fi +])# _LT_LINKER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) + + +# LT_CMD_MAX_LEN +#--------------- +AC_DEFUN([LT_CMD_MAX_LEN], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \ + = "XX$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac +]) +if test -n $lt_cv_sys_max_cmd_len ; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +max_cmd_len=$lt_cv_sys_max_cmd_len +_LT_DECL([], [max_cmd_len], [0], + [What is the maximum length of a command?]) +])# LT_CMD_MAX_LEN + +# Old name: +AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) + + +# _LT_HEADER_DLFCN +# ---------------- +m4_defun([_LT_HEADER_DLFCN], +[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl +])# _LT_HEADER_DLFCN + + +# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ---------------------------------------------------------------- +m4_defun([_LT_TRY_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "$cross_compiling" = yes; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +[#line __oline__ "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +}] +_LT_EOF + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_TRY_DLOPEN_SELF + + +# LT_SYS_DLOPEN_SELF +# ------------------ +AC_DEFUN([LT_SYS_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen="shl_load"], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen="dlopen"], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +_LT_DECL([dlopen_support], [enable_dlopen], [0], + [Whether dlopen is supported]) +_LT_DECL([dlopen_self], [enable_dlopen_self], [0], + [Whether dlopen of programs is supported]) +_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], + [Whether dlopen of statically linked programs is supported]) +])# LT_SYS_DLOPEN_SELF + +# Old name: +AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) + + +# _LT_COMPILER_C_O([TAGNAME]) +# --------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler. +# This macro does not hard code the compiler like AC_PROG_CC_C_O. +m4_defun([_LT_COMPILER_C_O], +[m4_require([_LT_DECL_SED])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . 2>&AS_MESSAGE_LOG_FD + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* +]) +_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], + [Does compiler simultaneously support -c and -o options?]) +])# _LT_COMPILER_C_O + + +# _LT_COMPILER_FILE_LOCKS([TAGNAME]) +# ---------------------------------- +# Check to see if we can do hard links to lock some files if needed +m4_defun([_LT_COMPILER_FILE_LOCKS], +[m4_require([_LT_ENABLE_LOCK])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_COMPILER_C_O([$1]) + +hard_links="nottested" +if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test "$hard_links" = no; then + AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) +])# _LT_COMPILER_FILE_LOCKS + + +# _LT_CHECK_OBJDIR +# ---------------- +m4_defun([_LT_CHECK_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +_LT_DECL([], [objdir], [0], + [The name of the directory that contains temporary libtool files])dnl +m4_pattern_allow([LT_OBJDIR])dnl +AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", + [Define to the sub-directory in which libtool stores uninstalled libraries.]) +])# _LT_CHECK_OBJDIR + + +# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) +# -------------------------------------- +# Check hardcoding attributes. +m4_defun([_LT_LINKER_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || + test -n "$_LT_TAGVAR(runpath_var, $1)" || + test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && + test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then + # Linking always hardcodes the temporary library directory. + _LT_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) + +if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || + test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi +_LT_TAGDECL([], [hardcode_action], [0], + [How to hardcode a shared library path into an executable]) +])# _LT_LINKER_HARDCODE_LIBPATH + + +# _LT_CMD_STRIPLIB +# ---------------- +m4_defun([_LT_CMD_STRIPLIB], +[m4_require([_LT_DECL_EGREP]) +striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) +_LT_DECL([], [striplib], [1]) +])# _LT_CMD_STRIPLIB + + +# _LT_SYS_DYNAMIC_LINKER([TAG]) +# ----------------------------- +# PORTME Fill in your ld.so characteristics +m4_defun([_LT_SYS_DYNAMIC_LINKER], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_OBJDUMP])dnl +m4_require([_LT_DECL_SED])dnl +AC_MSG_CHECKING([dynamic linker characteristics]) +m4_if([$1], + [], [ +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'` + else + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[[lt_foo]]++; } + if (lt_freq[[lt_foo]] == 1) { print lt_foo; } +}'` + sys_lib_search_path_spec=`$ECHO $lt_search_path_spec` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[[4-9]]*) + version_type=linux + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[[45]]*) + version_type=linux + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH printed by + # mingw gcc, but we are running on Cygwin. Gcc prints its search + # path with ; separators, and with drive letters. We can handle the + # drive letters (cygwin fileutils understands them), so leave them, + # especially as we might pass files found there to a mingw objdump, + # which wouldn't understand a cygwinified path. Ahh. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + ;; + esac + ;; + + *) + library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' + ;; + esac + dynamic_linker='Win32 ld.exe' + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd1*) + dynamic_linker=no + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[[123]]*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[[01]]* | freebsdelf3.[[01]]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ + freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +gnu*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555. + postinstall_cmds='chmod 555 $lib' + ;; + +interix[[3-9]]*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be Linux ELF. +linux* | k*bsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + # Some binutils ld are patched to set DT_RUNPATH + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ + LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], + [shlibpath_overrides_runpath=yes])]) + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[[89]] | openbsd2.[[89]].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + +_LT_DECL([], [variables_saved_for_relink], [1], + [Variables whose values should be saved in libtool wrapper scripts and + restored at link time]) +_LT_DECL([], [need_lib_prefix], [0], + [Do we need the "lib" prefix for modules?]) +_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) +_LT_DECL([], [version_type], [0], [Library versioning type]) +_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) +_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) +_LT_DECL([], [shlibpath_overrides_runpath], [0], + [Is shlibpath searched before the hard-coded library search path?]) +_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) +_LT_DECL([], [library_names_spec], [1], + [[List of archive names. First name is the real one, the rest are links. + The last name is the one that the linker finds with -lNAME]]) +_LT_DECL([], [soname_spec], [1], + [[The coded name of the library, if different from the real name]]) +_LT_DECL([], [postinstall_cmds], [2], + [Command to use after installation of a shared archive]) +_LT_DECL([], [postuninstall_cmds], [2], + [Command to use after uninstallation of a shared archive]) +_LT_DECL([], [finish_cmds], [2], + [Commands used to finish a libtool library installation in a directory]) +_LT_DECL([], [finish_eval], [1], + [[As "finish_cmds", except a single script fragment to be evaled but + not shown]]) +_LT_DECL([], [hardcode_into_libs], [0], + [Whether we should hardcode library paths into libraries]) +_LT_DECL([], [sys_lib_search_path_spec], [2], + [Compile-time system search path for libraries]) +_LT_DECL([], [sys_lib_dlsearch_path_spec], [2], + [Run-time system search path for libraries]) +])# _LT_SYS_DYNAMIC_LINKER + + +# _LT_PATH_TOOL_PREFIX(TOOL) +# -------------------------- +# find a file program which can recognize shared library +AC_DEFUN([_LT_PATH_TOOL_PREFIX], +[m4_require([_LT_DECL_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="m4_if([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$1; then + lt_cv_path_MAGIC_CMD="$ac_dir/$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac]) +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +_LT_DECL([], [MAGIC_CMD], [0], + [Used to examine libraries when file_magic_cmd begins with "file"])dnl +])# _LT_PATH_TOOL_PREFIX + +# Old name: +AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) + + +# _LT_PATH_MAGIC +# -------------- +# find a file program which can recognize a shared library +m4_defun([_LT_PATH_MAGIC], +[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# _LT_PATH_MAGIC + + +# LT_PATH_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([LT_PATH_LD], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl + +AC_ARG_WITH([gnu-ld], + [AS_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test "$withval" = no || with_gnu_ld=yes], + [with_gnu_ld=no])dnl + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[[3-9]]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be Linux ELF. +linux* | k*bsd*-gnu) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + +_LT_DECL([], [deplibs_check_method], [1], + [Method to check whether dependent libraries are shared objects]) +_LT_DECL([], [file_magic_cmd], [1], + [Command to use when deplibs_check_method == "file_magic"]) +])# _LT_CHECK_MAGIC_METHOD + + +# LT_PATH_NM +# ---------- +# find the pathname to a BSD- or MS-compatible name lister +AC_DEFUN([LT_PATH_NM], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_nm_to_check="${ac_tool_prefix}nm" + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/$lt_tmp_nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS="$lt_save_ifs" + done + : ${lt_cv_path_NM=no} +fi]) +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" +else + # Didn't find any BSD compatible name lister, look for dumpbin. + AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :) + AC_SUBST([DUMPBIN]) + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm +AC_SUBST([NM]) +_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl + +AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], + [lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD) + cat conftest.out >&AS_MESSAGE_LOG_FD + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest*]) +])# LT_PATH_NM + +# Old names: +AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) +AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_NM], []) +dnl AC_DEFUN([AC_PROG_NM], []) + + +# LT_LIB_M +# -------- +# check for math library +AC_DEFUN([LT_LIB_M], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM="-lm") + ;; +esac +AC_SUBST([LIBM]) +])# LT_LIB_M + +# Old name: +AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_CHECK_LIBM], []) + + +# _LT_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------- +m4_defun([_LT_COMPILER_NO_RTTI], +[m4_require([_LT_TAG_COMPILER])dnl + +_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test "$GCC" = yes; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + + _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], + [Compiler flag to turn off builtin functions]) +])# _LT_COMPILER_NO_RTTI + + +# _LT_CMD_GLOBAL_SYMBOLS +# ---------------------- +m4_defun([_LT_CMD_GLOBAL_SYMBOLS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_NM])dnl +AC_REQUIRE([LT_PATH_LD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_TAG_COMPILER])dnl + +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) + if test "$host_cpu" = ia64; then + symcode='[[ABCDEGRST]]' + fi + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris*) + symcode='[[BDRT]]' + ;; +sco3.2v5*) + symcode='[[DT]]' + ;; +sysv4.2uw2*) + symcode='[[DT]]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[[ABDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK ['"\ +" {last_section=section; section=\$ 3};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx]" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + nlist=conftest.nm + if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +const struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[[]] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_save_LIBS="$LIBS" + lt_save_CFLAGS="$CFLAGS" + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS="$lt_save_LIBS" + CFLAGS="$lt_save_CFLAGS" + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi + +_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], + [Take the output of nm and produce a listing of raw symbols and C names]) +_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], + [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_c_name_address], + [lt_cv_sys_global_symbol_to_c_name_address], [1], + [Transform the output of nm in a C name address pair]) +_LT_DECL([global_symbol_to_c_name_address_lib_prefix], + [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], + [Transform the output of nm in a C name address pair when lib prefix is needed]) +]) # _LT_CMD_GLOBAL_SYMBOLS + + +# _LT_COMPILER_PIC([TAGNAME]) +# --------------------------- +m4_defun([_LT_COMPILER_PIC], +[m4_require([_LT_TAG_COMPILER])dnl +_LT_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_TAGVAR(lt_prog_compiler_static, $1)= + +AC_MSG_CHECKING([for $compiler option to produce PIC]) +m4_if([$1], [CXX], [ + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix[[4-9]]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + dgux*) + case $cc_basename in + ec++*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu) + case $cc_basename in + KCC*) + # KAI C++ Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xlc* | xlC*) + # IBM XL 8.0 on PPC + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx*) + # Digital/Compaq C++ + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc*) + # Lucid + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test "$GCC" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + hpux9* | hpux10* | hpux11*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + linux* | k*bsd*-gnu) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' + _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' + ;; + pgcc* | pgf77* | pgf90* | pgf95*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xl*) + # IBM XL C 8.0/Fortran 10.1 on PPC + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + ;; + *Sun\ F*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='' + ;; + esac + ;; + esac + ;; + + newsos6) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + rdos*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + solaris*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + case $cc_basename in + f77* | f90* | f95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; + *) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; + esac + ;; + + sunos4*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + unicos*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + + uts4*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" + ;; +esac +AC_MSG_RESULT([$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) +_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], + [How to pass a linker flag through the compiler]) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], + [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], + [Additional compiler flags for building library objects]) + +# +# Check to make sure the static flag actually works. +# +wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" +_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], + _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), + $lt_tmp_static_flag, + [], + [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) +_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) +])# _LT_COMPILER_PIC + + +# _LT_LINKER_SHLIBS([TAGNAME]) +# ---------------------------- +# See if the linker supports building shared libraries. +m4_defun([_LT_LINKER_SHLIBS], +[AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +m4_if([$1], [CXX], [ + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + case $host_os in + aix[[4-9]]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + ;; + linux* | k*bsd*-gnu) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] +], [ + runpath_var= + _LT_TAGVAR(allow_undefined_flag, $1)= + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(archive_cmds, $1)= + _LT_TAGVAR(archive_expsym_cmds, $1)= + _LT_TAGVAR(compiler_needs_object, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(hardcode_automatic, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= + _LT_TAGVAR(hardcode_libdir_separator, $1)= + _LT_TAGVAR(hardcode_minus_L, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(inherit_rpath, $1)=no + _LT_TAGVAR(link_all_deplibs, $1)=unknown + _LT_TAGVAR(module_cmds, $1)= + _LT_TAGVAR(module_expsym_cmds, $1)= + _LT_TAGVAR(old_archive_from_new_cmds, $1)= + _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_TAGVAR(thread_safe_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. +dnl Note also adjust exclude_expsyms for C++ above. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + esac + + _LT_TAGVAR(ld_shlibs, $1)=yes + if test "$with_gnu_ld" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[[3-9]]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.9.1, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to modify your PATH +*** so that a non-GNU linker is found, and then restart. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag= + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + _LT_TAGVAR(whole_archive_flag_spec, $1)= + tmp_sharedflag='--shared' ;; + xl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + xlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir' + _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + sunos4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then + runpath_var= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GCC" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + _LT_TAGVAR(link_all_deplibs, $1)=no + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + bsdi[[45]]*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' + _LT_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + freebsd1*) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + AC_LINK_IFELSE(int foo(void) {}, + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + ) + LDFLAGS="$save_LDFLAGS" + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + else + case $host_os in + openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + ;; + esac + fi + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + solaris*) + _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + fi + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' + ;; + esac + fi + fi +]) +AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) +test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + +_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld + +_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl +_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl +_LT_DECL([], [extract_expsyms_cmds], [2], + [The commands to extract the exported symbol list from a shared archive]) + +# +# Do we need to explicitly link libc? +# +case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $_LT_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_MSG_CHECKING([whether -lc should be explicitly linked in]) + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) + pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) + _LT_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) + then + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + else + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + AC_MSG_RESULT([$_LT_TAGVAR(archive_cmds_need_lc, $1)]) + ;; + esac + fi + ;; +esac + +_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], + [Whether or not to add -lc for building shared libraries]) +_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], + [enable_shared_with_static_runtimes], [0], + [Whether or not to disallow shared libs when runtime libs are static]) +_LT_TAGDECL([], [export_dynamic_flag_spec], [1], + [Compiler flag to allow reflexive dlopens]) +_LT_TAGDECL([], [whole_archive_flag_spec], [1], + [Compiler flag to generate shared objects directly from archives]) +_LT_TAGDECL([], [compiler_needs_object], [1], + [Whether the compiler copes with passing no objects directly]) +_LT_TAGDECL([], [old_archive_from_new_cmds], [2], + [Create an old-style archive from a shared archive]) +_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], + [Create a temporary old-style archive to link instead of a shared archive]) +_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) +_LT_TAGDECL([], [archive_expsym_cmds], [2]) +_LT_TAGDECL([], [module_cmds], [2], + [Commands used to build a loadable module if different from building + a shared archive.]) +_LT_TAGDECL([], [module_expsym_cmds], [2]) +_LT_TAGDECL([], [with_gnu_ld], [1], + [Whether we are building with GNU ld or not]) +_LT_TAGDECL([], [allow_undefined_flag], [1], + [Flag that allows shared libraries with undefined symbols to be built]) +_LT_TAGDECL([], [no_undefined_flag], [1], + [Flag that enforces no undefined symbols]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], + [Flag to hardcode $libdir into a binary during linking. + This must work even if $libdir does not exist]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1], + [[If ld is used when linking, flag to hardcode $libdir into a binary + during linking. This must work even if $libdir does not exist]]) +_LT_TAGDECL([], [hardcode_libdir_separator], [1], + [Whether we need a single "-rpath" flag with a separated argument]) +_LT_TAGDECL([], [hardcode_direct], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary]) +_LT_TAGDECL([], [hardcode_direct_absolute], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary and the resulting library dependency is + "absolute", i.e impossible to change by setting ${shlibpath_var} if the + library is relocated]) +_LT_TAGDECL([], [hardcode_minus_L], [0], + [Set to "yes" if using the -LDIR flag during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_shlibpath_var], [0], + [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_automatic], [0], + [Set to "yes" if building a shared library automatically hardcodes DIR + into the library and all subsequent libraries and executables linked + against it]) +_LT_TAGDECL([], [inherit_rpath], [0], + [Set to yes if linker adds runtime paths of dependent libraries + to runtime path list]) +_LT_TAGDECL([], [link_all_deplibs], [0], + [Whether libtool must link a program against all its dependency libraries]) +_LT_TAGDECL([], [fix_srcfile_path], [1], + [Fix the shell variable $srcfile for the compiler]) +_LT_TAGDECL([], [always_export_symbols], [0], + [Set to "yes" if exported symbols are required]) +_LT_TAGDECL([], [export_symbols_cmds], [2], + [The commands to list exported symbols]) +_LT_TAGDECL([], [exclude_expsyms], [1], + [Symbols that should not be listed in the preloaded symbols]) +_LT_TAGDECL([], [include_expsyms], [1], + [Symbols that must always be exported]) +_LT_TAGDECL([], [prelink_cmds], [2], + [Commands necessary for linking programs (against libraries) with templates]) +_LT_TAGDECL([], [file_list_spec], [1], + [Specify filename containing input files]) +dnl FIXME: Not yet implemented +dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], +dnl [Compiler flag to generate thread safe objects]) +])# _LT_LINKER_SHLIBS + + +# _LT_LANG_C_CONFIG([TAG]) +# ------------------------ +# Ensure that the configuration variables for a C compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_C_CONFIG], +[m4_require([_LT_DECL_EGREP])dnl +lt_save_CC="$CC" +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + +_LT_TAG_COMPILER +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + LT_SYS_DLOPEN_SELF + _LT_CMD_STRIPLIB + + # Report which library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_CONFIG($1) +fi +AC_LANG_POP +CC="$lt_save_CC" +])# _LT_LANG_C_CONFIG + + +# _LT_PROG_CXX +# ------------ +# Since AC_PROG_CXX is broken, in that it returns g++ if there is no c++ +# compiler, we have our own version here. +m4_defun([_LT_PROG_CXX], +[ +pushdef([AC_MSG_ERROR], [_lt_caught_CXX_error=yes]) +AC_PROG_CXX +if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + AC_PROG_CXXCPP +else + _lt_caught_CXX_error=yes +fi +popdef([AC_MSG_ERROR]) +])# _LT_PROG_CXX + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([_LT_PROG_CXX], []) + + +# _LT_LANG_CXX_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a C++ compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_CXX_CONFIG], +[AC_REQUIRE([_LT_PROG_CXX])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl + +AC_LANG_PUSH(C++) +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(compiler_needs_object, $1)=no +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_caught_CXX_error" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + else + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + fi + + if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + LT_PATH_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) + _LT_TAGVAR(ld_shlibs, $1)=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GXX" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + _LT_SYS_MODULE_PATH_AIX + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + freebsd[[12]]*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + freebsd-elf*) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + gnu*) + ;; + + hpux9*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + *) + if test "$GXX" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib' + fi + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + ;; + + linux* | k*bsd*-gnu) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [[1-5]]* | *pgcpp\ [[1-5]]*) + _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"' + _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~ + $RANLIB $oldlib' + _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + *) # Version 6 will use weak symbols + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + xl*) + # IBM XL 8.0 on PPC, with GNU ld + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='echo' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + openbsd2*) + # C++ shared libraries are fairly broken + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=echo + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + cxx*) + case $host in + osf3*) + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + ;; + *) + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~ + $RM $lib.exp' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + + output_verbose_link_cmd='echo' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + fi + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) + test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + + _LT_TAGVAR(GCC, $1)="$GXX" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + CC=$lt_save_CC + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test "$_lt_caught_CXX_error" != yes + +AC_LANG_POP +])# _LT_LANG_CXX_CONFIG + + +# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) +# --------------------------------- +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +m4_defun([_LT_SYS_HIDDEN_LIBDEPS], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +# Dependencies to place before and after the object being linked: +_LT_TAGVAR(predep_objects, $1)= +_LT_TAGVAR(postdep_objects, $1)= +_LT_TAGVAR(predeps, $1)= +_LT_TAGVAR(postdeps, $1)= +_LT_TAGVAR(compiler_lib_search_path, $1)= + +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF +int a; +void foo (void) { a = 0; } +_LT_EOF +], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF +], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer*4 a + a=0 + return + end +_LT_EOF +], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer a + a=0 + return + end +_LT_EOF +], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF +public class foo { + private int a; + public void bar (void) { + a = 0; + } +}; +_LT_EOF +]) +dnl Parse the compiler output and extract the necessary +dnl objects, libraries and library flags. +if AC_TRY_EVAL(ac_compile); then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case $p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + else + prev= + fi + + if test "$pre_test_object_deps_done" = no; then + case $p in + -L* | -R*) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then + _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" + else + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$_LT_TAGVAR(postdeps, $1)"; then + _LT_TAGVAR(postdeps, $1)="${prev}${p}" + else + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" + fi + fi + ;; + + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test "$pre_test_object_deps_done" = no; then + if test -z "$_LT_TAGVAR(predep_objects, $1)"; then + _LT_TAGVAR(predep_objects, $1)="$p" + else + _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" + fi + else + if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then + _LT_TAGVAR(postdep_objects, $1)="$p" + else + _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling $1 test program" +fi + +$RM -f confest.$objext + +# PORTME: override above test on systems where it is broken +m4_if([$1], [CXX], +[case $host_os in +interix[[3-9]]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + _LT_TAGVAR(predep_objects,$1)= + _LT_TAGVAR(postdep_objects,$1)= + _LT_TAGVAR(postdeps,$1)= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; + +solaris*) + case $cc_basename in + CC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac +]) + +case " $_LT_TAGVAR(postdeps, $1) " in +*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; +esac + _LT_TAGVAR(compiler_lib_search_dirs, $1)= +if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi +_LT_TAGDECL([], [compiler_lib_search_dirs], [1], + [The directories searched by this compiler when creating a shared library]) +_LT_TAGDECL([], [predep_objects], [1], + [Dependencies to place before and after the objects being linked to + create a shared library]) +_LT_TAGDECL([], [postdep_objects], [1]) +_LT_TAGDECL([], [predeps], [1]) +_LT_TAGDECL([], [postdeps], [1]) +_LT_TAGDECL([], [compiler_lib_search_path], [1], + [The library search path used internally by the compiler when linking + a shared library]) +])# _LT_SYS_HIDDEN_LIBDEPS + + +# _LT_PROG_F77 +# ------------ +# Since AC_PROG_F77 is broken, in that it returns the empty string +# if there is no fortran compiler, we have our own version here. +m4_defun([_LT_PROG_F77], +[ +pushdef([AC_MSG_ERROR], [_lt_disable_F77=yes]) +AC_PROG_F77 +if test -z "$F77" || test "X$F77" = "Xno"; then + _lt_disable_F77=yes +fi +popdef([AC_MSG_ERROR]) +])# _LT_PROG_F77 + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([_LT_PROG_F77], []) + + +# _LT_LANG_F77_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a Fortran 77 compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_F77_CONFIG], +[AC_REQUIRE([_LT_PROG_F77])dnl +AC_LANG_PUSH(Fortran 77) + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for f77 test sources. +ac_ext=f + +# Object file extension for compiled f77 test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the F77 compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_F77" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + CC=${F77-"f77"} + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + GCC=$G77 + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$G77" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC="$lt_save_CC" +fi # test "$_lt_disable_F77" != yes + +AC_LANG_POP +])# _LT_LANG_F77_CONFIG + + +# _LT_PROG_FC +# ----------- +# Since AC_PROG_FC is broken, in that it returns the empty string +# if there is no fortran compiler, we have our own version here. +m4_defun([_LT_PROG_FC], +[ +pushdef([AC_MSG_ERROR], [_lt_disable_FC=yes]) +AC_PROG_FC +if test -z "$FC" || test "X$FC" = "Xno"; then + _lt_disable_FC=yes +fi +popdef([AC_MSG_ERROR]) +])# _LT_PROG_FC + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([_LT_PROG_FC], []) + + +# _LT_LANG_FC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for a Fortran compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_FC_CONFIG], +[AC_REQUIRE([_LT_PROG_FC])dnl +AC_LANG_PUSH(Fortran) + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for fc test sources. +ac_ext=${ac_fc_srcext-f} + +# Object file extension for compiled fc test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the FC compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_FC" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + CC=${FC-"f95"} + compiler=$CC + GCC=$ac_cv_fc_compiler_gnu + + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC="$lt_save_CC" +fi # test "$_lt_disable_FC" != yes + +AC_LANG_POP +])# _LT_LANG_FC_CONFIG + + +# _LT_LANG_GCJ_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Java Compiler compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_GCJ_CONFIG], +[AC_REQUIRE([LT_PROG_GCJ])dnl +AC_LANG_SAVE + +# Source file extension for Java test sources. +ac_ext=java + +# Object file extension for compiled Java test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}" + +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +lt_save_GCC=$GCC +GCC=yes +CC=${GCJ-"gcj"} +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)="$LD" +_LT_CC_BASENAME([$compiler]) + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC="$lt_save_CC" +])# _LT_LANG_GCJ_CONFIG + + +# _LT_LANG_RC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for the Windows resource compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_RC_CONFIG], +[AC_REQUIRE([LT_PROG_RC])dnl +AC_LANG_SAVE + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' + +# Code to be used in simple link tests +lt_simple_link_test_code="$lt_simple_compile_test_code" + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +lt_save_GCC=$GCC +GCC= +CC=${RC-"windres"} +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_CC_BASENAME([$compiler]) +_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + +if test -n "$compiler"; then + : + _LT_CONFIG($1) +fi + +GCC=$lt_save_GCC +AC_LANG_RESTORE +CC="$lt_save_CC" +])# _LT_LANG_RC_CONFIG + + +# LT_PROG_GCJ +# ----------- +AC_DEFUN([LT_PROG_GCJ], +[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], + [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], + [AC_CHECK_TOOL(GCJ, gcj,) + test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS)])])[]dnl +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_GCJ], []) + + +# LT_PROG_RC +# ---------- +AC_DEFUN([LT_PROG_RC], +[AC_CHECK_TOOL(RC, windres,) +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_RC], []) + + +# _LT_DECL_EGREP +# -------------- +# If we don't have a new enough Autoconf to choose the best grep +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_EGREP], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_REQUIRE([AC_PROG_FGREP])dnl +test -z "$GREP" && GREP=grep +_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) +_LT_DECL([], [EGREP], [1], [An ERE matcher]) +_LT_DECL([], [FGREP], [1], [A literal string matcher]) +dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too +AC_SUBST([GREP]) +]) + + +# _LT_DECL_OBJDUMP +# -------------- +# If we don't have a new enough Autoconf to choose the best objdump +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_OBJDUMP], +[AC_CHECK_TOOL(OBJDUMP, objdump, false) +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) +AC_SUBST([OBJDUMP]) +]) + + +# _LT_DECL_SED +# ------------ +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +m4_defun([_LT_DECL_SED], +[AC_PROG_SED +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" +_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) +_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], + [Sed that helps us avoid accidentally triggering echo(1) options like -n]) +])# _LT_DECL_SED + +m4_ifndef([AC_PROG_SED], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ + +m4_defun([AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +IFS=$as_save_IFS +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f $lt_ac_sed && continue + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test $lt_ac_count -gt 10 && break + lt_ac_count=`expr $lt_ac_count + 1` + if test $lt_ac_count -gt $lt_ac_max; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_SUBST([SED]) +AC_MSG_RESULT([$SED]) +])#AC_PROG_SED +])#m4_ifndef + +# Old name: +AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_SED], []) + + +# _LT_CHECK_SHELL_FEATURES +# ------------------------ +# Find out whether the shell is Bourne or XSI compatible, +# or has some other useful features. +m4_defun([_LT_CHECK_SHELL_FEATURES], +[AC_MSG_CHECKING([whether the shell understands some XSI constructs]) +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +AC_MSG_RESULT([$xsi_shell]) +_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) + +AC_MSG_CHECKING([whether the shell understands "+="]) +lt_shell_append=no +( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +AC_MSG_RESULT([$lt_shell_append]) +_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi +_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac +_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl +_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl +])# _LT_CHECK_SHELL_FEATURES + + +# _LT_PROG_XSI_SHELLFNS +# --------------------- +# Bourne and XSI compatible variants of some useful shell functions. +m4_defun([_LT_PROG_XSI_SHELLFNS], +[case $xsi_shell in + yes) + cat << \_LT_EOF >> "$cfgfile" + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac +} + +# func_basename file +func_basename () +{ + func_basename_result="${1##*/}" +} + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac + func_basename_result="${1##*/}" +} + +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +func_stripname () +{ + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary parameter first. + func_stripname_result=${3} + func_stripname_result=${func_stripname_result#"${1}"} + func_stripname_result=${func_stripname_result%"${2}"} +} + +# func_opt_split +func_opt_split () +{ + func_opt_split_opt=${1%%=*} + func_opt_split_arg=${1#*=} +} + +# func_lo2o object +func_lo2o () +{ + case ${1} in + *.lo) func_lo2o_result=${1%.lo}.${objext} ;; + *) func_lo2o_result=${1} ;; + esac +} + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=${1%.*}.lo +} + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=$(( $[*] )) +} + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=${#1} +} + +_LT_EOF + ;; + *) # Bourne compatible functions. + cat << \_LT_EOF >> "$cfgfile" + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi +} + +# func_basename file +func_basename () +{ + func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` +} + +dnl func_dirname_and_basename +dnl A portable version of this function is already defined in general.m4sh +dnl so there is no need for it here. + +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# func_strip_suffix prefix name +func_stripname () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "X${3}" \ + | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "X${3}" \ + | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;; + esac +} + +# sed scripts: +my_sed_long_opt='1s/^\(-[[^=]]*\)=.*/\1/;q' +my_sed_long_arg='1s/^-[[^=]]*=//' + +# func_opt_split +func_opt_split () +{ + func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"` + func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"` +} + +# func_lo2o object +func_lo2o () +{ + func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"` +} + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[[^.]]*$/.lo/'` +} + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=`expr "$[@]"` +} + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=`expr "$[1]" : ".*" 2>/dev/null || echo $max_cmd_len` +} + +_LT_EOF +esac + +case $lt_shell_append in + yes) + cat << \_LT_EOF >> "$cfgfile" + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "$[1]+=\$[2]" +} +_LT_EOF + ;; + *) + cat << \_LT_EOF >> "$cfgfile" + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "$[1]=\$$[1]\$[2]" +} + +_LT_EOF + ;; + esac +]) diff --git a/2.5.13/2.5.x/apache2/build/ltmain.sh b/2.5.13/2.5.x/apache2/build/ltmain.sh new file mode 100755 index 00000000..b612e9a6 --- /dev/null +++ b/2.5.13/2.5.x/apache2/build/ltmain.sh @@ -0,0 +1,8412 @@ +# Generated from ltmain.m4sh. + +# ltmain.sh (GNU libtool) 2.2.6 +# Written by Gordon Matzigkeit , 1996 + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 2008 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, +# or obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Usage: $progname [OPTION]... [MODE-ARG]... +# +# Provide generalized library-building support services. +# +# --config show all configuration variables +# --debug enable verbose shell tracing +# -n, --dry-run display commands without modifying any files +# --features display basic configuration information and exit +# --mode=MODE use operation mode MODE +# --preserve-dup-deps don't remove duplicate dependency libraries +# --quiet, --silent don't print informational messages +# --tag=TAG use configuration variables from tag TAG +# -v, --verbose print informational messages (default) +# --version print version information +# -h, --help print short or long help message +# +# MODE must be one of the following: +# +# clean remove files from the build directory +# compile compile a source file into a libtool object +# execute automatically set library path, then run a program +# finish complete the installation of libtool libraries +# install install libraries or executables +# link create a library or an executable +# uninstall remove libraries from an installed directory +# +# MODE-ARGS vary depending on the MODE. +# Try `$progname --help --mode=MODE' for a more detailed description of MODE. +# +# When reporting a bug, please describe a test case to reproduce it and +# include the following information: +# +# host-triplet: $host +# shell: $SHELL +# compiler: $LTCC +# compiler flags: $LTCFLAGS +# linker: $LD (gnu? $with_gnu_ld) +# $progname: (GNU libtool) 2.2.6 Debian-2.2.6a-1ubuntu1 +# automake: $automake_version +# autoconf: $autoconf_version +# +# Report bugs to . + +PROGRAM=ltmain.sh +PACKAGE=libtool +VERSION="2.2.6 Debian-2.2.6a-1ubuntu1" +TIMESTAMP="" +package_revision=1.3012 + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# NLS nuisances: We save the old values to restore during execute mode. +# Only set LANG and LC_ALL to C if already set. +# These must not be set unconditionally because not all systems understand +# e.g. LANG=C (notably SCO). +lt_user_locale= +lt_safe_locale= +for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test \"\${$lt_var+set}\" = set; then + save_$lt_var=\$$lt_var + $lt_var=C + export $lt_var + lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" + lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" + fi" +done + +$lt_unset CDPATH + + + + + +: ${CP="cp -f"} +: ${ECHO="echo"} +: ${EGREP="/bin/grep -E"} +: ${FGREP="/bin/grep -F"} +: ${GREP="/bin/grep"} +: ${LN_S="ln -s"} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SED="/bin/sed"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} +: ${Xsed="$SED -e 1s/^X//"} + +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +exit_status=$EXIT_SUCCESS + +# Make sure IFS has a sensible default +lt_nl=' +' +IFS=" $lt_nl" + +dirname="s,/[^/]*$,," +basename="s,^.*/,," + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi + func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` +} + +# Generated shell functions inserted here. + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath="$0" + +# The name of this program: +# In the unlikely event $progname began with a '-', it would play havoc with +# func_echo (imagine progname=-n), so we prepend ./ in that case: +func_dirname_and_basename "$progpath" +progname=$func_basename_result +case $progname in + -*) progname=./$progname ;; +esac + +# Make sure we have an absolute path for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=$func_dirname_result + progdir=`cd "$progdir" && pwd` + progpath="$progdir/$progname" + ;; + *) + save_IFS="$IFS" + IFS=: + for progdir in $PATH; do + IFS="$save_IFS" + test -x "$progdir/$progname" && break + done + IFS="$save_IFS" + test -n "$progdir" || progdir=`pwd` + progpath="$progdir/$progname" + ;; +esac + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed="${SED}"' -e 1s/^X//' +sed_quote_subst='s/\([`"$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Re-`\' parameter expansions in output of double_quote_subst that were +# `\'-ed in input to the same. If an odd number of `\' preceded a '$' +# in input to double_quote_subst, that '$' was protected from expansion. +# Since each input `\' is now two `\'s, look for any number of runs of +# four `\'s followed by two `\'s and then a '$'. `\' that '$'. +bs='\\' +bs2='\\\\' +bs4='\\\\\\\\' +dollar='\$' +sed_double_backslash="\ + s/$bs4/&\\ +/g + s/^$bs2$dollar/$bs&/ + s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g + s/\n//g" + +# Standard options: +opt_dry_run=false +opt_help=false +opt_quiet=false +opt_verbose=false +opt_warning=: + +# func_echo arg... +# Echo program name prefixed message, along with the current mode +# name if it has been set yet. +func_echo () +{ + $ECHO "$progname${mode+: }$mode: $*" +} + +# func_verbose arg... +# Echo program name prefixed message in verbose mode only. +func_verbose () +{ + $opt_verbose && func_echo ${1+"$@"} + + # A bug in bash halts the script if the last line of a function + # fails when set -e is in force, so we need another command to + # work around that: + : +} + +# func_error arg... +# Echo program name prefixed message to standard error. +func_error () +{ + $ECHO "$progname${mode+: }$mode: "${1+"$@"} 1>&2 +} + +# func_warning arg... +# Echo program name prefixed warning message to standard error. +func_warning () +{ + $opt_warning && $ECHO "$progname${mode+: }$mode: warning: "${1+"$@"} 1>&2 + + # bash bug again: + : +} + +# func_fatal_error arg... +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + +# func_fatal_help arg... +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + func_error ${1+"$@"} + func_fatal_error "$help" +} +help="Try \`$progname --help' for more information." ## default + + +# func_grep expression filename +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_mkdir_p directory-path +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + my_directory_path="$1" + my_dir_list= + + if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then + + # Protect directory names starting with `-' + case $my_directory_path in + -*) my_directory_path="./$my_directory_path" ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$my_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + my_dir_list="$my_directory_path:$my_dir_list" + + # If the last portion added has no slash in it, the list is done + case $my_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + my_directory_path=`$ECHO "X$my_directory_path" | $Xsed -e "$dirname"` + done + my_dir_list=`$ECHO "X$my_dir_list" | $Xsed -e 's,:*$,,'` + + save_mkdir_p_IFS="$IFS"; IFS=':' + for my_dir in $my_dir_list; do + IFS="$save_mkdir_p_IFS" + # mkdir can fail with a `File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$my_dir" 2>/dev/null || : + done + IFS="$save_mkdir_p_IFS" + + # Bail out if we (or some other process) failed to create a directory. + test -d "$my_directory_path" || \ + func_fatal_error "Failed to create \`$1'" + fi +} + + +# func_mktempdir [string] +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, STRING is the basename for that directory. +func_mktempdir () +{ + my_template="${TMPDIR-/tmp}/${1-$progname}" + + if test "$opt_dry_run" = ":"; then + # Return a directory name, but don't create it in dry-run mode + my_tmpdir="${my_template}-$$" + else + + # If mktemp works, use that first and foremost + my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` + + if test ! -d "$my_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + my_tmpdir="${my_template}-${RANDOM-0}$$" + + save_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$my_tmpdir" + umask $save_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$my_tmpdir" || \ + func_fatal_error "cannot create temporary directory \`$my_tmpdir'" + fi + + $ECHO "X$my_tmpdir" | $Xsed +} + + +# func_quote_for_eval arg +# Aesthetically quote ARG to be evaled later. +# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT +# is double-quoted, suitable for a subsequent eval, whereas +# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters +# which are still active within double quotes backslashified. +func_quote_for_eval () +{ + case $1 in + *[\\\`\"\$]*) + func_quote_for_eval_unquoted_result=`$ECHO "X$1" | $Xsed -e "$sed_quote_subst"` ;; + *) + func_quote_for_eval_unquoted_result="$1" ;; + esac + + case $func_quote_for_eval_unquoted_result in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and and variable + # expansion for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" + ;; + *) + func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" + esac +} + + +# func_quote_for_expand arg +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + case $1 in + *[\\\`\"]*) + my_arg=`$ECHO "X$1" | $Xsed \ + -e "$double_quote_subst" -e "$sed_double_backslash"` ;; + *) + my_arg="$1" ;; + esac + + case $my_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + my_arg="\"$my_arg\"" + ;; + esac + + func_quote_for_expand_result="$my_arg" +} + + +# func_show_eval cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$my_cmd" + my_status=$? + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + + +# func_show_eval_locale cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$lt_user_locale + $my_cmd" + my_status=$? + eval "$lt_safe_locale" + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + + + + + +# func_version +# Echo version message to standard output and exit. +func_version () +{ + $SED -n '/^# '$PROGRAM' (GNU /,/# warranty; / { + s/^# // + s/^# *$// + s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ + p + }' < "$progpath" + exit $? +} + +# func_usage +# Echo short help message to standard output and exit. +func_usage () +{ + $SED -n '/^# Usage:/,/# -h/ { + s/^# // + s/^# *$// + s/\$progname/'$progname'/ + p + }' < "$progpath" + $ECHO + $ECHO "run \`$progname --help | more' for full usage" + exit $? +} + +# func_help +# Echo long help message to standard output and exit. +func_help () +{ + $SED -n '/^# Usage:/,/# Report bugs to/ { + s/^# // + s/^# *$// + s*\$progname*'$progname'* + s*\$host*'"$host"'* + s*\$SHELL*'"$SHELL"'* + s*\$LTCC*'"$LTCC"'* + s*\$LTCFLAGS*'"$LTCFLAGS"'* + s*\$LD*'"$LD"'* + s/\$with_gnu_ld/'"$with_gnu_ld"'/ + s/\$automake_version/'"`(automake --version) 2>/dev/null |$SED 1q`"'/ + s/\$autoconf_version/'"`(autoconf --version) 2>/dev/null |$SED 1q`"'/ + p + }' < "$progpath" + exit $? +} + +# func_missing_arg argname +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + func_error "missing argument for $1" + exit_cmd=exit +} + +exit_cmd=: + + + + + +# Check that we have a working $ECHO. +if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X$1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t'; then + # Yippee, $ECHO works! + : +else + # Restart under the correct shell, and then maybe $ECHO will work. + exec $SHELL "$progpath" --no-reexec ${1+"$@"} +fi + +if test "X$1" = X--fallback-echo; then + # used as fallback echo + shift + cat </dev/null 2>&1; then + taglist="$taglist $tagname" + + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac +} + +# Parse options once, thoroughly. This comes as soon as possible in +# the script to make things like `libtool --version' happen quickly. +{ + + # Shorthand for --mode=foo, only valid as the first argument + case $1 in + clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift + ;; + compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift + ;; + execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift + ;; + finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift + ;; + install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; + link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; + uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift + ;; + esac + + # Parse non-mode specific arguments: + while test "$#" -gt 0; do + opt="$1" + shift + + case $opt in + --config) func_config ;; + + --debug) preserve_args="$preserve_args $opt" + func_echo "enabling shell trace mode" + opt_debug='set -x' + $opt_debug + ;; + + -dlopen) test "$#" -eq 0 && func_missing_arg "$opt" && break + execute_dlfiles="$execute_dlfiles $1" + shift + ;; + + --dry-run | -n) opt_dry_run=: ;; + --features) func_features ;; + --finish) mode="finish" ;; + + --mode) test "$#" -eq 0 && func_missing_arg "$opt" && break + case $1 in + # Valid mode arguments: + clean) ;; + compile) ;; + execute) ;; + finish) ;; + install) ;; + link) ;; + relink) ;; + uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $opt" + exit_cmd=exit + break + ;; + esac + + mode="$1" + shift + ;; + + --preserve-dup-deps) + opt_duplicate_deps=: ;; + + --quiet|--silent) preserve_args="$preserve_args $opt" + opt_silent=: + ;; + + --verbose| -v) preserve_args="$preserve_args $opt" + opt_silent=false + ;; + + --tag) test "$#" -eq 0 && func_missing_arg "$opt" && break + preserve_args="$preserve_args $opt $1" + func_enable_tag "$1" # tagname is set here + shift + ;; + + # Separate optargs to long options: + -dlopen=*|--mode=*|--tag=*) + func_opt_split "$opt" + set dummy "$func_opt_split_opt" "$func_opt_split_arg" ${1+"$@"} + shift + ;; + + -\?|-h) func_usage ;; + --help) opt_help=: ;; + --version) func_version ;; + + -*) func_fatal_help "unrecognized option \`$opt'" ;; + + *) nonopt="$opt" + break + ;; + esac + done + + + case $host in + *cygwin* | *mingw* | *pw32* | *cegcc*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_duplicate_deps + ;; + esac + + # Having warned about all mis-specified options, bail out if + # anything was wrong. + $exit_cmd $EXIT_FAILURE +} + +# func_check_version_match +# Ensure that we are using m4 macros, and libtool script from the same +# release of libtool. +func_check_version_match () +{ + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from an older release. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + fi + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, +$progname: but the definition of this LT_INIT comes from revision $macro_revision. +$progname: You should recreate aclocal.m4 with macros from revision $package_revision +$progname: of $PACKAGE $VERSION and run autoconf again. +_LT_EOF + fi + + exit $EXIT_MISMATCH + fi +} + + +## ----------- ## +## Main. ## +## ----------- ## + +$opt_help || { + # Sanity checks first: + func_check_version_match + + if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then + func_fatal_configuration "not configured to build any kind of library" + fi + + test -z "$mode" && func_fatal_error "error: you must specify a MODE." + + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$execute_dlfiles" && test "$mode" != execute; then + func_error "unrecognized option \`-dlopen'" + $ECHO "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$progname --help --mode=$mode' for more information." +} + + +# func_lalib_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_lalib_p () +{ + test -f "$1" && + $SED -e 4q "$1" 2>/dev/null \ + | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} + +# func_lalib_unsafe_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function implements the same check as func_lalib_p without +# resorting to external programs. To this end, it redirects stdin and +# closes it afterwards, without saving the original file descriptor. +# As a safety measure, use it only where a negative result would be +# fatal anyway. Works if `file' does not exist. +func_lalib_unsafe_p () +{ + lalib_p=no + if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then + for lalib_p_l in 1 2 3 4 + do + read lalib_p_line + case "$lalib_p_line" in + \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; + esac + done + exec 0<&5 5<&- + fi + test "$lalib_p" = yes +} + +# func_ltwrapper_script_p file +# True iff FILE is a libtool wrapper script +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_script_p () +{ + func_lalib_p "$1" +} + +# func_ltwrapper_executable_p file +# True iff FILE is a libtool wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_executable_p () +{ + func_ltwrapper_exec_suffix= + case $1 in + *.exe) ;; + *) func_ltwrapper_exec_suffix=.exe ;; + esac + $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 +} + +# func_ltwrapper_scriptname file +# Assumes file is an ltwrapper_executable +# uses $file to determine the appropriate filename for a +# temporary ltwrapper_script. +func_ltwrapper_scriptname () +{ + func_ltwrapper_scriptname_result="" + if func_ltwrapper_executable_p "$1"; then + func_dirname_and_basename "$1" "" "." + func_stripname '' '.exe' "$func_basename_result" + func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" + fi +} + +# func_ltwrapper_p file +# True iff FILE is a libtool wrapper script or wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_p () +{ + func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" +} + + +# func_execute_cmds commands fail_cmd +# Execute tilde-delimited COMMANDS. +# If FAIL_CMD is given, eval that upon failure. +# FAIL_CMD may read-access the current command in variable CMD! +func_execute_cmds () +{ + $opt_debug + save_ifs=$IFS; IFS='~' + for cmd in $1; do + IFS=$save_ifs + eval cmd=\"$cmd\" + func_show_eval "$cmd" "${2-:}" + done + IFS=$save_ifs +} + + +# func_source file +# Source FILE, adding directory component if necessary. +# Note that it is not necessary on cygwin/mingw to append a dot to +# FILE even if both FILE and FILE.exe exist: automatic-append-.exe +# behavior happens only for exec(3), not for open(2)! Also, sourcing +# `FILE.' does not work on cygwin managed mounts. +func_source () +{ + $opt_debug + case $1 in + */* | *\\*) . "$1" ;; + *) . "./$1" ;; + esac +} + + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + $opt_debug + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + func_quote_for_eval "$arg" + CC_quoted="$CC_quoted $func_quote_for_eval_result" + done + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + func_quote_for_eval "$arg" + CC_quoted="$CC_quoted $func_quote_for_eval_result" + done + case "$@ " in + " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + func_echo "unable to infer tagged configuration" + func_fatal_error "specify a tag with \`--tag'" +# else +# func_verbose "using $tagname tagged configuration" + fi + ;; + esac + fi +} + + + +# func_write_libtool_object output_name pic_name nonpic_name +# Create a libtool object file (analogous to a ".la" file), +# but don't create it if we're doing a dry run. +func_write_libtool_object () +{ + write_libobj=${1} + if test "$build_libtool_libs" = yes; then + write_lobj=\'${2}\' + else + write_lobj=none + fi + + if test "$build_old_libs" = yes; then + write_oldobj=\'${3}\' + else + write_oldobj=none + fi + + $opt_dry_run || { + cat >${write_libobj}T <?"'"'"' &()|`$[]' \ + && func_warning "libobj name \`$libobj' may not contain shell special characters." + func_dirname_and_basename "$obj" "/" "" + objname="$func_basename_result" + xdir="$func_dirname_result" + lobj=${xdir}$objdir/$objname + + test -z "$base_compile" && \ + func_fatal_help "you must specify a compilation command" + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2* | cegcc*) + pic_mode=default + ;; + esac + if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$ECHO "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + $ECHO "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + removelist="$removelist $output_obj" + $ECHO "$srcfile" > "$lockfile" + fi + + $opt_dry_run || $RM $removelist + removelist="$removelist $lockfile" + trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 + + if test -n "$fix_srcfile_path"; then + eval srcfile=\"$fix_srcfile_path\" + fi + func_quote_for_eval "$srcfile" + qsrcfile=$func_quote_for_eval_result + + # Only build a PIC object if we are building libtool libraries. + if test "$build_libtool_libs" = yes; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + if test "$pic_mode" != no; then + command="$base_compile $qsrcfile $pic_flag" + else + # Don't build PIC code + command="$base_compile $qsrcfile" + fi + + func_mkdir_p "$xdir$objdir" + + if test -z "$output_obj"; then + # Place PIC objects in $objdir + command="$command -o $lobj" + fi + + func_show_eval_locale "$command" \ + 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + func_show_eval '$MV "$output_obj" "$lobj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + + # Allow error messages only from the first compilation. + if test "$suppress_opt" = yes; then + suppress_output=' >/dev/null 2>&1' + fi + fi + + # Only build a position-dependent object if we build old libraries. + if test "$build_old_libs" = yes; then + if test "$pic_mode" != yes; then + # Don't build PIC code + command="$base_compile $qsrcfile$pie_flag" + else + command="$base_compile $qsrcfile $pic_flag" + fi + if test "$compiler_c_o" = yes; then + command="$command -o $obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + command="$command$suppress_output" + func_show_eval_locale "$command" \ + '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + func_show_eval '$MV "$output_obj" "$obj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + fi + + $opt_dry_run || { + func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" + + # Unlock the critical section if it was locked + if test "$need_locks" != no; then + removelist=$lockfile + $RM "$lockfile" + fi + } + + exit $EXIT_SUCCESS +} + +$opt_help || { +test "$mode" = compile && func_mode_compile ${1+"$@"} +} + +func_mode_help () +{ + # We need to display help for each of the modes. + case $mode in + "") + # Generic help is extracted from the usage comments + # at the start of this file. + func_help + ;; + + clean) + $ECHO \ +"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + + compile) + $ECHO \ +"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -no-suppress do not suppress compiler output for multiple passes + -prefer-pic try to building PIC objects only + -prefer-non-pic try to building non-PIC objects only + -shared do not build a \`.o' file suitable for static linking + -static only build a \`.o' file suitable for static linking + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + + execute) + $ECHO \ +"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + + finish) + $ECHO \ +"Usage: $progname [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + + install) + $ECHO \ +"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The following components of INSTALL-COMMAND are treated specially: + + -inst-prefix PREFIX-DIR Use PREFIX-DIR as a staging area for installation + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + + link) + $ECHO \ +"Usage: $progname [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE Use a list of object files found in FILE to specify objects + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -shared only do dynamic linking of libtool libraries + -shrext SUFFIX override the standard shared library file extension + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + -weak LIBNAME declare that the target provides the LIBNAME interface + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. + +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." + ;; + + uninstall) + $ECHO \ +"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + + *) + func_fatal_help "invalid operation mode \`$mode'" + ;; + esac + + $ECHO + $ECHO "Try \`$progname --help' for more information about other modes." + + exit $? +} + + # Now that we've collected a possible --mode arg, show help if necessary + $opt_help && func_mode_help + + +# func_mode_execute arg... +func_mode_execute () +{ + $opt_debug + # The first argument is the command name. + cmd="$nonopt" + test -z "$cmd" && \ + func_fatal_help "you must specify a COMMAND" + + # Handle -dlopen flags immediately. + for file in $execute_dlfiles; do + test -f "$file" \ + || func_fatal_help "\`$file' is not a file" + + dir= + case $file in + *.la) + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$lib' is not a valid libtool archive" + + # Read the libtool library. + dlname= + library_names= + func_source "$file" + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && \ + func_warning "\`$file' was not linked with \`-export-dynamic'" + continue + fi + + func_dirname "$file" "" "." + dir="$func_dirname_result" + + if test -f "$dir/$objdir/$dlname"; then + dir="$dir/$objdir" + else + if test ! -f "$dir/$dlname"; then + func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" + fi + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + func_dirname "$file" "" "." + dir="$func_dirname_result" + ;; + + *) + func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -*) ;; + *) + # Do a test to see if this is really a libtool program. + if func_ltwrapper_script_p "$file"; then + func_source "$file" + # Transform arg to wrapped name. + file="$progdir/$program" + elif func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + func_source "$func_ltwrapper_scriptname_result" + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + func_quote_for_eval "$file" + args="$args $func_quote_for_eval_result" + done + + if test "X$opt_dry_run" = Xfalse; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + else + $lt_unset $lt_var + fi" + done + + # Now prepare to actually exec the command. + exec_cmd="\$cmd$args" + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" + $ECHO "export $shlibpath_var" + fi + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + fi +} + +test "$mode" = execute && func_mode_execute ${1+"$@"} + + +# func_mode_finish arg... +func_mode_finish () +{ + $opt_debug + libdirs="$nonopt" + admincmds= + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for dir + do + libdirs="$libdirs $dir" + done + + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + func_execute_cmds "$finish_cmds" 'admincmds="$admincmds +'"$cmd"'"' + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $opt_dry_run || eval "$cmds" || admincmds="$admincmds + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + $opt_silent && exit $EXIT_SUCCESS + + $ECHO "X----------------------------------------------------------------------" | $Xsed + $ECHO "Libraries have been installed in:" + for libdir in $libdirs; do + $ECHO " $libdir" + done + $ECHO + $ECHO "If you ever happen to want to link against installed libraries" + $ECHO "in a given directory, LIBDIR, you must either use libtool, and" + $ECHO "specify the full pathname of the library, or use the \`-LLIBDIR'" + $ECHO "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + $ECHO " - add LIBDIR to the \`$shlibpath_var' environment variable" + $ECHO " during execution" + fi + if test -n "$runpath_var"; then + $ECHO " - add LIBDIR to the \`$runpath_var' environment variable" + $ECHO " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $ECHO " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + $ECHO " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + $ECHO " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + $ECHO + + $ECHO "See any operating system documentation about shared libraries for" + case $host in + solaris2.[6789]|solaris2.1[0-9]) + $ECHO "more information, such as the ld(1), crle(1) and ld.so(8) manual" + $ECHO "pages." + ;; + *) + $ECHO "more information, such as the ld(1) and ld.so(8) manual pages." + ;; + esac + $ECHO "X----------------------------------------------------------------------" | $Xsed + exit $EXIT_SUCCESS +} + +test "$mode" = finish && func_mode_finish ${1+"$@"} + + +# func_mode_install arg... +func_mode_install () +{ + $opt_debug + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + # Allow the use of GNU shtool's install command. + $ECHO "X$nonopt" | $GREP shtool >/dev/null; then + # Aesthetically quote it. + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " + arg=$1 + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + func_quote_for_eval "$arg" + install_prog="$install_prog$func_quote_for_eval_result" + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + for arg + do + if test -n "$dest"; then + files="$files $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) + case " $install_prog " in + *[\\\ /]cp\ *) ;; + *) prev=$arg ;; + esac + ;; + -g | -m | -o) + prev=$arg + ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + func_quote_for_eval "$arg" + install_prog="$install_prog $func_quote_for_eval_result" + done + + test -z "$install_prog" && \ + func_fatal_help "you must specify an install program" + + test -n "$prev" && \ + func_fatal_help "the \`$prev' option requires an argument" + + if test -z "$files"; then + if test -z "$dest"; then + func_fatal_help "no file or destination specified" + else + func_fatal_help "you must specify a destination" + fi + fi + + # Strip any trailing slash from the destination. + func_stripname '' '/' "$dest" + dest=$func_stripname_result + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + func_dirname_and_basename "$dest" "" "." + destdir="$func_dirname_result" + destname="$func_basename_result" + + # Not a directory, so check to see that there is only one file specified. + set dummy $files; shift + test "$#" -gt 1 && \ + func_fatal_help "\`$dest' is not a directory" + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + func_fatal_help "\`$destdir' must be an absolute directory name" + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + staticlibs="$staticlibs $file" + ;; + + *.la) + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$file' is not a valid libtool archive" + + library_names= + old_library= + relink_command= + func_source "$file" + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) current_libdirs="$current_libdirs $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) future_libdirs="$future_libdirs $libdir" ;; + esac + fi + + func_dirname "$file" "/" "" + dir="$func_dirname_result" + dir="$dir$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$ECHO "X$destdir" | $Xsed -e "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + test "$inst_prefix_dir" = "$destdir" && \ + func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%%"` + fi + + func_warning "relinking \`$file'" + func_show_eval "$relink_command" \ + 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' + fi + + # See the names of the shared library. + set dummy $library_names; shift + if test -n "$1"; then + realname="$1" + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + func_show_eval "$install_prog $dir/$srcname $destdir/$realname" \ + 'exit $?' + tstripme="$stripme" + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + case $realname in + *.dll.a) + tstripme="" + ;; + esac + ;; + esac + if test -n "$tstripme" && test -n "$striplib"; then + func_show_eval "$striplib $destdir/$realname" 'exit $?' + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try `ln -sf' first, because the `ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + test "$linkname" != "$realname" \ + && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + func_execute_cmds "$postinstall_cmds" 'exit $?' + fi + + # Install the pseudo-library for information purposes. + func_basename "$file" + name="$func_basename_result" + instname="$dir/$name"i + func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' + + # Maybe install the static library, too. + test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + func_lo2o "$destfile" + staticdest=$func_lo2o_result + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + func_fatal_help "cannot copy a libtool object to \`$destfile'" + ;; + esac + + # Install the libtool object if requested. + test -n "$destfile" && \ + func_show_eval "$install_prog $file $destfile" 'exit $?' + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + func_lo2o "$file" + staticobj=$func_lo2o_result + func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' + fi + exit $EXIT_SUCCESS + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext="" + case $file in + *.exe) + if test ! -f "$file"; then + func_stripname '' '.exe' "$file" + file=$func_stripname_result + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin* | *mingw*) + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + wrapper=$func_ltwrapper_scriptname_result + else + func_stripname '' '.exe' "$file" + wrapper=$func_stripname_result + fi + ;; + *) + wrapper=$file + ;; + esac + if func_ltwrapper_script_p "$wrapper"; then + notinst_deplibs= + relink_command= + + func_source "$wrapper" + + # Check the variables that should have been set. + test -z "$generated_by_libtool_version" && \ + func_fatal_error "invalid libtool wrapper script \`$wrapper'" + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + func_source "$lib" + fi + libfile="$libdir/"`$ECHO "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + func_warning "\`$lib' has not been installed in \`$libdir'" + finalize=no + fi + done + + relink_command= + func_source "$wrapper" + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + $opt_dry_run || { + if test "$finalize" = yes; then + tmpdir=`func_mktempdir` + func_basename "$file$stripped_ext" + file="$func_basename_result" + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$ECHO "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` + + $opt_silent || { + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" + } + if eval "$relink_command"; then : + else + func_error "error: relink \`$file' with the above command before installing it" + $opt_dry_run || ${RM}r "$tmpdir" + continue + fi + file="$outputname" + else + func_warning "cannot relink \`$file'" + fi + } + else + # Install the binary that we compiled earlier. + file=`$ECHO "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + func_stripname '' '.exe' "$destfile" + destfile=$func_stripname_result + ;; + esac + ;; + esac + func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' + $opt_dry_run || if test -n "$outputname"; then + ${RM}r "$tmpdir" + fi + ;; + esac + done + + for file in $staticlibs; do + func_basename "$file" + name="$func_basename_result" + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + + func_show_eval "$install_prog \$file \$oldlib" 'exit $?' + + if test -n "$stripme" && test -n "$old_striplib"; then + func_show_eval "$old_striplib $oldlib" 'exit $?' + fi + + # Do each command in the postinstall commands. + func_execute_cmds "$old_postinstall_cmds" 'exit $?' + done + + test -n "$future_libdirs" && \ + func_warning "remember to run \`$progname --finish$future_libdirs'" + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + $opt_dry_run && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi +} + +test "$mode" = install && func_mode_install ${1+"$@"} + + +# func_generate_dlsyms outputname originator pic_p +# Extract symbols from dlprefiles and create ${outputname}S.o with +# a dlpreopen symbol table. +func_generate_dlsyms () +{ + $opt_debug + my_outputname="$1" + my_originator="$2" + my_pic_p="${3-no}" + my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` + my_dlsyms= + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + my_dlsyms="${my_outputname}S.c" + else + func_error "not configured to extract global symbols from dlpreopened files" + fi + fi + + if test -n "$my_dlsyms"; then + case $my_dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${my_outputname}.nm" + + func_show_eval "$RM $nlist ${nlist}S ${nlist}T" + + # Parse the name list into a source file. + func_verbose "creating $output_objdir/$my_dlsyms" + + $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ +/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + func_verbose "generating symbol list for \`$output'" + + $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$ECHO "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + for progfile in $progfiles; do + func_verbose "extracting global C symbols from \`$progfile'" + $opt_dry_run || eval "$NM $progfile | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $opt_dry_run || { + eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + if test -n "$export_symbols_regex"; then + $opt_dry_run || { + eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$outputname.exp" + $opt_dry_run || { + $RM $export_symbols + eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + } + else + $opt_dry_run || { + eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + case $host in + *cygwin | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + } + fi + fi + + for dlprefile in $dlprefiles; do + func_verbose "extracting global C symbols from \`$dlprefile'" + func_basename "$dlprefile" + name="$func_basename_result" + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + done + + $opt_dry_run || { + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $MV "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if $GREP -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + $GREP -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' + else + $ECHO '/* NONE */' >> "$output_objdir/$my_dlsyms" + fi + + $ECHO >> "$output_objdir/$my_dlsyms" "\ + +/* The mapping between symbol names and symbols. */ +typedef struct { + const char *name; + void *address; +} lt_dlsymlist; +" + case $host in + *cygwin* | *mingw* | *cegcc* ) + $ECHO >> "$output_objdir/$my_dlsyms" "\ +/* DATA imports from DLLs on WIN32 con't be const, because + runtime relocations are performed -- see ld's documentation + on pseudo-relocs. */" + lt_dlsym_const= ;; + *osf5*) + echo >> "$output_objdir/$my_dlsyms" "\ +/* This system does not cope well with relocations in const data */" + lt_dlsym_const= ;; + *) + lt_dlsym_const=const ;; + esac + + $ECHO >> "$output_objdir/$my_dlsyms" "\ +extern $lt_dlsym_const lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[]; +$lt_dlsym_const lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[] = +{\ + { \"$my_originator\", (void *) 0 }," + + case $need_lib_prefix in + no) + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + *) + eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + esac + $ECHO >> "$output_objdir/$my_dlsyms" "\ + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_${my_prefix}_LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + } # !$opt_dry_run + + pic_flag_for_symtable= + case "$compile_command " in + *" -static "*) ;; + *) + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; + *-*-hpux*) + pic_flag_for_symtable=" $pic_flag" ;; + *) + if test "X$my_pic_p" != Xno; then + pic_flag_for_symtable=" $pic_flag" + fi + ;; + esac + ;; + esac + symtab_cflags= + for arg in $LTCFLAGS; do + case $arg in + -pie | -fpie | -fPIE) ;; + *) symtab_cflags="$symtab_cflags $arg" ;; + esac + done + + # Now compile the dynamic symbol file. + func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' + + # Clean up the generated files. + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' + + # Transform the symbol file into the correct name. + symfileobj="$output_objdir/${my_outputname}S.$objext" + case $host in + *cygwin* | *mingw* | *cegcc* ) + if test -f "$output_objdir/$my_outputname.def"; then + compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + else + compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` + fi + ;; + *) + compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` + ;; + esac + ;; + *) + func_fatal_error "unknown suffix for \`$my_dlsyms'" + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$ECHO "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` + finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` + fi +} + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +func_win32_libid () +{ + $opt_debug + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | + $EGREP 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then + win32_nmres=`eval $NM -f posix -A $1 | + $SED -n -e ' + 1,100{ + / I /{ + s,.*,import, + p + q + } + }'` + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $ECHO "$win32_libid_type" +} + + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + $opt_debug + f_ex_an_ar_dir="$1"; shift + f_ex_an_ar_oldlib="$1" + func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" 'exit $?' + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" + fi +} + + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + $opt_debug + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + func_basename "$my_xlib" + my_xlib="$func_basename_result" + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + func_arith $extracted_serial + 1 + extracted_serial=$func_arith_result + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir="$my_gentop/$my_xlib_u" + + func_mkdir_p "$my_xdir" + + case $host in + *-darwin*) + func_verbose "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + $opt_dry_run || { + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + darwin_base_archive=`basename "$darwin_archive"` + darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` + if test -n "$darwin_arches"; then + darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + func_extract_an_archive "`pwd`" "${darwin_base_archive}" + cd "$darwin_curdir" + $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + done # $darwin_arches + ## Okay now we've a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` + $LIPO -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + $RM -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + } # !$opt_dry_run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` + done + + func_extract_archives_result="$my_oldobjs" +} + + + +# func_emit_wrapper_part1 [arg=no] +# +# Emit the first part of a libtool wrapper script on stdout. +# For more information, see the description associated with +# func_emit_wrapper(), below. +func_emit_wrapper_part1 () +{ + func_emit_wrapper_part1_arg1=no + if test -n "$1" ; then + func_emit_wrapper_part1_arg1=$1 + fi + + $ECHO "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='${SED} -e 1s/^X//' +sed_quote_subst='$sed_quote_subst' + +# Be Bourne compatible +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variables: + generated_by_libtool_version='$macro_version' + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$ECHO are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + ECHO=\"$qecho\" + file=\"\$0\" + # Make sure echo works. + if test \"X\$1\" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift + elif test \"X\`{ \$ECHO '\t'; } 2>/dev/null\`\" = 'X\t'; then + # Yippee, \$ECHO works! + : + else + # Restart under the correct shell, and then maybe \$ECHO will work. + exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} + fi + fi\ +" + $ECHO "\ + + # Find the directory that this script lives in. + thisdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$ECHO \"X\$file\" | \$Xsed -e 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` + done +" +} +# end: func_emit_wrapper_part1 + +# func_emit_wrapper_part2 [arg=no] +# +# Emit the second part of a libtool wrapper script on stdout. +# For more information, see the description associated with +# func_emit_wrapper(), below. +func_emit_wrapper_part2 () +{ + func_emit_wrapper_part2_arg1=no + if test -n "$1" ; then + func_emit_wrapper_part2_arg1=$1 + fi + + $ECHO "\ + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_part2_arg1 + if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then + # special case for '.' + if test \"\$thisdir\" = \".\"; then + thisdir=\`pwd\` + fi + # remove .libs from thisdir + case \"\$thisdir\" in + *[\\\\/]$objdir ) thisdir=\`\$ECHO \"X\$thisdir\" | \$Xsed -e 's%[\\\\/][^\\\\/]*$%%'\` ;; + $objdir ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $ECHO "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $MKDIR \"\$progdir\" + else + $RM \"\$progdir/\$file\" + fi" + + $ECHO "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + $ECHO \"\$relink_command_output\" >&2 + $RM \"\$progdir/\$file\" + exit 1 + fi + fi + + $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $RM \"\$progdir/\$program\"; + $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $RM \"\$progdir/\$file\" + fi" + else + $ECHO "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $ECHO "\ + + if test -f \"\$progdir/\$program\"; then" + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $ECHO "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$ECHO \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` + + export $shlibpath_var +" + fi + + # fixup the dll searchpath if we need to. + if test -n "$dllsearchpath"; then + $ECHO "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + $ECHO "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2* | *-cegcc*) + $ECHO "\ + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $ECHO "\ + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $ECHO "\ + \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 + exit 1 + fi + else + # The program doesn't exist. + \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 + \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 + $ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" +} +# end: func_emit_wrapper_part2 + + +# func_emit_wrapper [arg=no] +# +# Emit a libtool wrapper script on stdout. +# Don't directly open a file because we may want to +# incorporate the script contents within a cygwin/mingw +# wrapper executable. Must ONLY be called from within +# func_mode_link because it depends on a number of variables +# set therein. +# +# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR +# variable will take. If 'yes', then the emitted script +# will assume that the directory in which it is stored is +# the $objdir directory. This is a cygwin/mingw-specific +# behavior. +func_emit_wrapper () +{ + func_emit_wrapper_arg1=no + if test -n "$1" ; then + func_emit_wrapper_arg1=$1 + fi + + # split this up so that func_emit_cwrapperexe_src + # can call each part independently. + func_emit_wrapper_part1 "${func_emit_wrapper_arg1}" + func_emit_wrapper_part2 "${func_emit_wrapper_arg1}" +} + + +# func_to_host_path arg +# +# Convert paths to host format when used with build tools. +# Intended for use with "native" mingw (where libtool itself +# is running under the msys shell), or in the following cross- +# build environments: +# $build $host +# mingw (msys) mingw [e.g. native] +# cygwin mingw +# *nix + wine mingw +# where wine is equipped with the `winepath' executable. +# In the native mingw case, the (msys) shell automatically +# converts paths for any non-msys applications it launches, +# but that facility isn't available from inside the cwrapper. +# Similar accommodations are necessary for $host mingw and +# $build cygwin. Calling this function does no harm for other +# $host/$build combinations not listed above. +# +# ARG is the path (on $build) that should be converted to +# the proper representation for $host. The result is stored +# in $func_to_host_path_result. +func_to_host_path () +{ + func_to_host_path_result="$1" + if test -n "$1" ; then + case $host in + *mingw* ) + lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + case $build in + *mingw* ) # actually, msys + # awkward: cmd appends spaces to result + lt_sed_strip_trailing_spaces="s/[ ]*\$//" + func_to_host_path_tmp1=`( cmd //c echo "$1" |\ + $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""` + func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ + $SED -e "$lt_sed_naive_backslashify"` + ;; + *cygwin* ) + func_to_host_path_tmp1=`cygpath -w "$1"` + func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ + $SED -e "$lt_sed_naive_backslashify"` + ;; + * ) + # Unfortunately, winepath does not exit with a non-zero + # error code, so we are forced to check the contents of + # stdout. On the other hand, if the command is not + # found, the shell will set an exit code of 127 and print + # *an error message* to stdout. So we must check for both + # error code of zero AND non-empty stdout, which explains + # the odd construction: + func_to_host_path_tmp1=`winepath -w "$1" 2>/dev/null` + if test "$?" -eq 0 && test -n "${func_to_host_path_tmp1}"; then + func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ + $SED -e "$lt_sed_naive_backslashify"` + else + # Allow warning below. + func_to_host_path_result="" + fi + ;; + esac + if test -z "$func_to_host_path_result" ; then + func_error "Could not determine host path corresponding to" + func_error " '$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback: + func_to_host_path_result="$1" + fi + ;; + esac + fi +} +# end: func_to_host_path + +# func_to_host_pathlist arg +# +# Convert pathlists to host format when used with build tools. +# See func_to_host_path(), above. This function supports the +# following $build/$host combinations (but does no harm for +# combinations not listed here): +# $build $host +# mingw (msys) mingw [e.g. native] +# cygwin mingw +# *nix + wine mingw +# +# Path separators are also converted from $build format to +# $host format. If ARG begins or ends with a path separator +# character, it is preserved (but converted to $host format) +# on output. +# +# ARG is a pathlist (on $build) that should be converted to +# the proper representation on $host. The result is stored +# in $func_to_host_pathlist_result. +func_to_host_pathlist () +{ + func_to_host_pathlist_result="$1" + if test -n "$1" ; then + case $host in + *mingw* ) + lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + # Remove leading and trailing path separator characters from + # ARG. msys behavior is inconsistent here, cygpath turns them + # into '.;' and ';.', and winepath ignores them completely. + func_to_host_pathlist_tmp2="$1" + # Once set for this call, this variable should not be + # reassigned. It is used in tha fallback case. + func_to_host_pathlist_tmp1=`echo "$func_to_host_pathlist_tmp2" |\ + $SED -e 's|^:*||' -e 's|:*$||'` + case $build in + *mingw* ) # Actually, msys. + # Awkward: cmd appends spaces to result. + lt_sed_strip_trailing_spaces="s/[ ]*\$//" + func_to_host_pathlist_tmp2=`( cmd //c echo "$func_to_host_pathlist_tmp1" |\ + $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""` + func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\ + $SED -e "$lt_sed_naive_backslashify"` + ;; + *cygwin* ) + func_to_host_pathlist_tmp2=`cygpath -w -p "$func_to_host_pathlist_tmp1"` + func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\ + $SED -e "$lt_sed_naive_backslashify"` + ;; + * ) + # unfortunately, winepath doesn't convert pathlists + func_to_host_pathlist_result="" + func_to_host_pathlist_oldIFS=$IFS + IFS=: + for func_to_host_pathlist_f in $func_to_host_pathlist_tmp1 ; do + IFS=$func_to_host_pathlist_oldIFS + if test -n "$func_to_host_pathlist_f" ; then + func_to_host_path "$func_to_host_pathlist_f" + if test -n "$func_to_host_path_result" ; then + if test -z "$func_to_host_pathlist_result" ; then + func_to_host_pathlist_result="$func_to_host_path_result" + else + func_to_host_pathlist_result="$func_to_host_pathlist_result;$func_to_host_path_result" + fi + fi + fi + IFS=: + done + IFS=$func_to_host_pathlist_oldIFS + ;; + esac + if test -z "$func_to_host_pathlist_result" ; then + func_error "Could not determine the host path(s) corresponding to" + func_error " '$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback. This may break if $1 contains DOS-style drive + # specifications. The fix is not to complicate the expression + # below, but for the user to provide a working wine installation + # with winepath so that path translation in the cross-to-mingw + # case works properly. + lt_replace_pathsep_nix_to_dos="s|:|;|g" + func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp1" |\ + $SED -e "$lt_replace_pathsep_nix_to_dos"` + fi + # Now, add the leading and trailing path separators back + case "$1" in + :* ) func_to_host_pathlist_result=";$func_to_host_pathlist_result" + ;; + esac + case "$1" in + *: ) func_to_host_pathlist_result="$func_to_host_pathlist_result;" + ;; + esac + ;; + esac + fi +} +# end: func_to_host_pathlist + +# func_emit_cwrapperexe_src +# emit the source code for a wrapper executable on stdout +# Must ONLY be called from within func_mode_link because +# it depends on a number of variable set therein. +func_emit_cwrapperexe_src () +{ + cat < +#include +#ifdef _MSC_VER +# include +# include +# include +# define setmode _setmode +#else +# include +# include +# ifdef __CYGWIN__ +# include +# define HAVE_SETENV +# ifdef __STRICT_ANSI__ +char *realpath (const char *, char *); +int putenv (char *); +int setenv (const char *, const char *, int); +# endif +# endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef S_IXOTH +# define S_IXOTH 0 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0 +#endif + +#ifdef _MSC_VER +# define S_IXUSR _S_IEXEC +# define stat _stat +# ifndef _INTPTR_T_DEFINED +# define intptr_t int +# endif +#endif + +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +# define HAVE_DOS_BASED_FILE_SYSTEM +# define FOPEN_WB "wb" +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_SEPARATOR_2 ';' +# endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#ifdef __CYGWIN__ +# define FOPEN_WB "wb" +#endif + +#ifndef FOPEN_WB +# define FOPEN_WB "w" +#endif +#ifndef _O_BINARY +# define _O_BINARY 0 +#endif + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +#undef LTWRAPPER_DEBUGPRINTF +#if defined DEBUGWRAPPER +# define LTWRAPPER_DEBUGPRINTF(args) ltwrapper_debugprintf args +static void +ltwrapper_debugprintf (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + (void) vfprintf (stderr, fmt, args); + va_end (args); +} +#else +# define LTWRAPPER_DEBUGPRINTF(args) +#endif + +const char *program_name = NULL; + +void *xmalloc (size_t num); +char *xstrdup (const char *string); +const char *base_name (const char *name); +char *find_executable (const char *wrapper); +char *chase_symlinks (const char *pathspec); +int make_executable (const char *path); +int check_executable (const char *path); +char *strendzap (char *str, const char *pat); +void lt_fatal (const char *message, ...); +void lt_setenv (const char *name, const char *value); +char *lt_extend_str (const char *orig_value, const char *add, int to_end); +void lt_opt_process_env_set (const char *arg); +void lt_opt_process_env_prepend (const char *arg); +void lt_opt_process_env_append (const char *arg); +int lt_split_name_value (const char *arg, char** name, char** value); +void lt_update_exe_path (const char *name, const char *value); +void lt_update_lib_path (const char *name, const char *value); + +static const char *script_text_part1 = +EOF + + func_emit_wrapper_part1 yes | + $SED -e 's/\([\\"]\)/\\\1/g' \ + -e 's/^/ "/' -e 's/$/\\n"/' + echo ";" + cat <"))); + for (i = 0; i < newargc; i++) + { + LTWRAPPER_DEBUGPRINTF (("(main) newargz[%d] : %s\n", i, (newargz[i] ? newargz[i] : ""))); + } + +EOF + + case $host_os in + mingw*) + cat <<"EOF" + /* execv doesn't actually work on mingw as expected on unix */ + rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz); + if (rval == -1) + { + /* failed to start process */ + LTWRAPPER_DEBUGPRINTF (("(main) failed to launch target \"%s\": errno = %d\n", lt_argv_zero, errno)); + return 127; + } + return rval; +EOF + ;; + *) + cat <<"EOF" + execv (lt_argv_zero, newargz); + return rval; /* =127, but avoids unused variable warning */ +EOF + ;; + esac + + cat <<"EOF" +} + +void * +xmalloc (size_t num) +{ + void *p = (void *) malloc (num); + if (!p) + lt_fatal ("Memory exhausted"); + + return p; +} + +char * +xstrdup (const char *string) +{ + return string ? strcpy ((char *) xmalloc (strlen (string) + 1), + string) : NULL; +} + +const char * +base_name (const char *name) +{ + const char *base; + +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + /* Skip over the disk name in MSDOS pathnames. */ + if (isalpha ((unsigned char) name[0]) && name[1] == ':') + name += 2; +#endif + + for (base = name; *name; name++) + if (IS_DIR_SEPARATOR (*name)) + base = name + 1; + return base; +} + +int +check_executable (const char *path) +{ + struct stat st; + + LTWRAPPER_DEBUGPRINTF (("(check_executable) : %s\n", + path ? (*path ? path : "EMPTY!") : "NULL!")); + if ((!path) || (!*path)) + return 0; + + if ((stat (path, &st) >= 0) + && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return 1; + else + return 0; +} + +int +make_executable (const char *path) +{ + int rval = 0; + struct stat st; + + LTWRAPPER_DEBUGPRINTF (("(make_executable) : %s\n", + path ? (*path ? path : "EMPTY!") : "NULL!")); + if ((!path) || (!*path)) + return 0; + + if (stat (path, &st) >= 0) + { + rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); + } + return rval; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise + Does not chase symlinks, even on platforms that support them. +*/ +char * +find_executable (const char *wrapper) +{ + int has_slash = 0; + const char *p; + const char *p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + int tmp_len; + char *concat_name; + + LTWRAPPER_DEBUGPRINTF (("(find_executable) : %s\n", + wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!")); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char *path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char *q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR (*q)) + break; + p_len = q - p; + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + tmp_len = strlen (tmp); + concat_name = + XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = + XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + tmp_len = strlen (tmp); + concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + return NULL; +} + +char * +chase_symlinks (const char *pathspec) +{ +#ifndef S_ISLNK + return xstrdup (pathspec); +#else + char buf[LT_PATHMAX]; + struct stat s; + char *tmp_pathspec = xstrdup (pathspec); + char *p; + int has_symlinks = 0; + while (strlen (tmp_pathspec) && !has_symlinks) + { + LTWRAPPER_DEBUGPRINTF (("checking path component for symlinks: %s\n", + tmp_pathspec)); + if (lstat (tmp_pathspec, &s) == 0) + { + if (S_ISLNK (s.st_mode) != 0) + { + has_symlinks = 1; + break; + } + + /* search backwards for last DIR_SEPARATOR */ + p = tmp_pathspec + strlen (tmp_pathspec) - 1; + while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + p--; + if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + { + /* no more DIR_SEPARATORS left */ + break; + } + *p = '\0'; + } + else + { + char *errstr = strerror (errno); + lt_fatal ("Error accessing file %s (%s)", tmp_pathspec, errstr); + } + } + XFREE (tmp_pathspec); + + if (!has_symlinks) + { + return xstrdup (pathspec); + } + + tmp_pathspec = realpath (pathspec, buf); + if (tmp_pathspec == 0) + { + lt_fatal ("Could not follow symlinks for %s", pathspec); + } + return xstrdup (tmp_pathspec); +#endif +} + +char * +strendzap (char *str, const char *pat) +{ + size_t len, patlen; + + assert (str != NULL); + assert (pat != NULL); + + len = strlen (str); + patlen = strlen (pat); + + if (patlen <= len) + { + str += len - patlen; + if (strcmp (str, pat) == 0) + *str = '\0'; + } + return str; +} + +static void +lt_error_core (int exit_status, const char *mode, + const char *message, va_list ap) +{ + fprintf (stderr, "%s: %s: ", program_name, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, "FATAL", message, ap); + va_end (ap); +} + +void +lt_setenv (const char *name, const char *value) +{ + LTWRAPPER_DEBUGPRINTF (("(lt_setenv) setting '%s' to '%s'\n", + (name ? name : ""), + (value ? value : ""))); + { +#ifdef HAVE_SETENV + /* always make a copy, for consistency with !HAVE_SETENV */ + char *str = xstrdup (value); + setenv (name, str, 1); +#else + int len = strlen (name) + 1 + strlen (value) + 1; + char *str = XMALLOC (char, len); + sprintf (str, "%s=%s", name, value); + if (putenv (str) != EXIT_SUCCESS) + { + XFREE (str); + } +#endif + } +} + +char * +lt_extend_str (const char *orig_value, const char *add, int to_end) +{ + char *new_value; + if (orig_value && *orig_value) + { + int orig_value_len = strlen (orig_value); + int add_len = strlen (add); + new_value = XMALLOC (char, add_len + orig_value_len + 1); + if (to_end) + { + strcpy (new_value, orig_value); + strcpy (new_value + orig_value_len, add); + } + else + { + strcpy (new_value, add); + strcpy (new_value + add_len, orig_value); + } + } + else + { + new_value = xstrdup (add); + } + return new_value; +} + +int +lt_split_name_value (const char *arg, char** name, char** value) +{ + const char *p; + int len; + if (!arg || !*arg) + return 1; + + p = strchr (arg, (int)'='); + + if (!p) + return 1; + + *value = xstrdup (++p); + + len = strlen (arg) - strlen (*value); + *name = XMALLOC (char, len); + strncpy (*name, arg, len-1); + (*name)[len - 1] = '\0'; + + return 0; +} + +void +lt_opt_process_env_set (const char *arg) +{ + char *name = NULL; + char *value = NULL; + + if (lt_split_name_value (arg, &name, &value) != 0) + { + XFREE (name); + XFREE (value); + lt_fatal ("bad argument for %s: '%s'", env_set_opt, arg); + } + + lt_setenv (name, value); + XFREE (name); + XFREE (value); +} + +void +lt_opt_process_env_prepend (const char *arg) +{ + char *name = NULL; + char *value = NULL; + char *new_value = NULL; + + if (lt_split_name_value (arg, &name, &value) != 0) + { + XFREE (name); + XFREE (value); + lt_fatal ("bad argument for %s: '%s'", env_prepend_opt, arg); + } + + new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + XFREE (name); + XFREE (value); +} + +void +lt_opt_process_env_append (const char *arg) +{ + char *name = NULL; + char *value = NULL; + char *new_value = NULL; + + if (lt_split_name_value (arg, &name, &value) != 0) + { + XFREE (name); + XFREE (value); + lt_fatal ("bad argument for %s: '%s'", env_append_opt, arg); + } + + new_value = lt_extend_str (getenv (name), value, 1); + lt_setenv (name, new_value); + XFREE (new_value); + XFREE (name); + XFREE (value); +} + +void +lt_update_exe_path (const char *name, const char *value) +{ + LTWRAPPER_DEBUGPRINTF (("(lt_update_exe_path) modifying '%s' by prepending '%s'\n", + (name ? name : ""), + (value ? value : ""))); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + /* some systems can't cope with a ':'-terminated path #' */ + int len = strlen (new_value); + while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) + { + new_value[len-1] = '\0'; + } + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +void +lt_update_lib_path (const char *name, const char *value) +{ + LTWRAPPER_DEBUGPRINTF (("(lt_update_lib_path) modifying '%s' by prepending '%s'\n", + (name ? name : ""), + (value ? value : ""))); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + } +} + + +EOF +} +# end: func_emit_cwrapperexe_src + +# func_mode_link arg... +func_mode_link () +{ + $opt_debug + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # which system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll which has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args=$nonopt + base_compile="$nonopt $@" + compile_command=$nonopt + finalize_command=$nonopt + + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + new_inherited_linker_flags= + + avoid_version=no + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + non_pic_objects= + precious_files_regex= + prefer_static_libs=no + preload=no + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + weak_libs= + single_module="${wl}-single_module" + func_infer_tag $base_compile + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + break + ;; + -all-static | -static | -static-libtool-libs) + case $arg in + -all-static) + if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + func_warning "complete static linking is impossible in this configuration" + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + esac + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + shift + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + func_append libtool_args " $func_quote_for_eval_result" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + func_append compile_command " @OUTPUT@" + func_append finalize_command " @OUTPUT@" + ;; + esac + + case $prev in + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + func_append compile_command " @SYMFILE@" + func_append finalize_command " @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + else + dlprefiles="$dlprefiles $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + test -f "$arg" \ + || func_fatal_error "symbol file \`$arg' does not exist" + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + framework) + case $host in + *-*-darwin*) + case "$deplibs " in + *" $qarg.ltframework "*) ;; + *) deplibs="$deplibs $qarg.ltframework" # this is fixed later + ;; + esac + ;; + esac + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat "$save_arg"` + do +# moreargs="$moreargs $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + done + else + func_fatal_error "link input file \`$arg' does not exist" + fi + arg=$save_arg + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) rpath="$rpath $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) xrpath="$xrpath $arg" ;; + esac + fi + prev= + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + weak) + weak_libs="$weak_libs $arg" + prev= + continue + ;; + xcclinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xcompiler) + compiler_flags="$compiler_flags $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xlinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $wl$qarg" + prev= + func_append compile_command " $wl$qarg" + func_append finalize_command " $wl$qarg" + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg="$arg" + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + # See comment for -static flag below, for more details. + func_append compile_command " $link_static_flag" + func_append finalize_command " $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + func_fatal_error "\`-allow-undefined' must not be used because it is the default" + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + func_fatal_error "more than one -exported-symbols argument is not allowed" + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework) + prev=framework + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + func_append compile_command " $arg" + func_append finalize_command " $arg" + ;; + esac + continue + ;; + + -L*) + func_stripname '-L' '' "$arg" + dir=$func_stripname_result + if test -z "$dir"; then + if test "$#" -gt 0; then + func_fatal_error "require no space between \`-L' and \`$1'" + else + func_fatal_error "need path for \`-L' option" + fi + fi + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + test -z "$absdir" && \ + func_fatal_error "cannot determine absolute directory name of \`$dir'" + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "*) ;; + *) + deplibs="$deplibs -L$dir" + lib_search_path="$lib_search_path $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "X$dir" | $Xsed -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + ::) dllsearchpath=$dir;; + *) dllsearchpath="$dllsearchpath:$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) dllsearchpath="$dllsearchpath:$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + deplibs="$deplibs System.ltframework" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test "X$arg" = "X-lc" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test "X$arg" = "X-lc" && continue + ;; + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + deplibs="$deplibs $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + # Darwin uses the -arch flag to determine output architecture. + -model|-arch|-isysroot) + compiler_flags="$compiler_flags $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads) + compiler_flags="$compiler_flags $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case "$new_inherited_linker_flags " in + *" $arg "*) ;; + * ) new_inherited_linker_flags="$new_inherited_linker_flags $arg" ;; + esac + continue + ;; + + -multi_module) + single_module="${wl}-multi_module" + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) + # The PATH hackery in wrapper scripts is required on Windows + # and Darwin in order for the loader to find any dlls it needs. + func_warning "\`-no-install' is ignored for $host" + func_warning "assuming \`-no-fast-install' instead" + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + func_stripname '-R' '' "$arg" + dir=$func_stripname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + continue + ;; + + -shared) + # The effects of -shared are defined in a previous loop. + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -static | -static-libtool-libs) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -weak) + prev=weak + continue + ;; + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + arg="$arg $wl$func_quote_for_eval_result" + compiler_flags="$compiler_flags $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Wl,*) + func_stripname '-Wl,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + arg="$arg $wl$func_quote_for_eval_result" + compiler_flags="$compiler_flags $wl$func_quote_for_eval_result" + linker_flags="$linker_flags $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # -msg_* for osf cc + -msg_*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + # -64, -mips[0-9] enable 64-bit mode on the SGI compiler + # -r[0-9][0-9]* specifies the processor on the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler + # +DA*, +DD* enable 64-bit mode on the HP compiler + # -q* pass through compiler args for the IBM compiler + # -m*, -t[45]*, -txscale* pass through architecture-specific + # compiler args for GCC + # -F/path gives path to uninstalled frameworks, gcc on darwin + # -p, -pg, --coverage, -fprofile-* pass through profiling flag for GCC + # @file GCC response files + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ + -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + func_append compile_command " $arg" + func_append finalize_command " $arg" + compiler_flags="$compiler_flags $arg" + continue + ;; + + # Some other compiler flag. + -* | +*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + *.$objext) + # A standard object. + objs="$objs $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + ;; + + *.$libext) + # An archive. + deplibs="$deplibs $arg" + old_deplibs="$old_deplibs $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + dlfiles="$dlfiles $arg" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + dlprefiles="$dlprefiles $arg" + prev= + else + deplibs="$deplibs $arg" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + done # argument parsing loop + + test -n "$prev" && \ + func_fatal_help "the \`$prevarg' option requires an argument" + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + func_basename "$output" + outputname="$func_basename_result" + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$ECHO \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + func_dirname "$output" "/" "" + output_objdir="$func_dirname_result$objdir" + # Create the object directory. + func_mkdir_p "$output_objdir" + + # Determine the type of output + case $output in + "") + func_fatal_help "you must specify an output file" + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if $opt_duplicate_deps ; then + case "$libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + libs="$libs $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if $opt_duplicate_compiler_generated_deps; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; + esac + pre_post_deps="$pre_post_deps $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + + case $linkmode in + lib) + passes="conv dlpreopen link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + + for pass in $passes; do + # The preopen pass in lib mode reverses $deplibs; put it back here + # so that -L comes before libs that need it for instance... + if test "$linkmode,$pass" = "lib,link"; then + ## FIXME: Find the place where the list is rebuilt in the wrong + ## order, and fix it there properly + tmp_deplibs= + for deplib in $deplibs; do + tmp_deplibs="$deplib $tmp_deplibs" + done + deplibs="$tmp_deplibs" + fi + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) + libs="$deplibs %DEPLIBS%" + test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" + ;; + esac + fi + if test "$linkmode,$pass" = "lib,dlpreopen"; then + # Collect and forward deplibs of preopened libtool libs + for lib in $dlprefiles; do + # Ignore non-libtool-libs + dependency_libs= + case $lib in + *.la) func_source "$lib" ;; + esac + + # Collect preopened libtool deplibs, except any this library + # has declared as weak libs + for deplib in $dependency_libs; do + deplib_base=`$ECHO "X$deplib" | $Xsed -e "$basename"` + case " $weak_libs " in + *" $deplib_base "*) ;; + *) deplibs="$deplibs $deplib" ;; + esac + done + done + libs="$dlprefiles" + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + + for deplib in $libs; do + lib= + found=no + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + compiler_flags="$compiler_flags $deplib" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;; + esac + fi + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + func_warning "\`-l' is ignored for archives/objects" + continue + fi + func_stripname '-l' '' "$deplib" + name=$func_stripname_result + if test "$linkmode" = lib; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib="$searchdir/lib${name}${search_ext}" + if test -f "$lib"; then + if test "$search_ext" = ".la"; then + found=yes + else + found=no + fi + break 2 + fi + done + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if func_lalib_p "$lib"; then + library_names= + old_library= + func_source "$lib" + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + *.ltframework) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;; + esac + fi + fi + continue + ;; + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + func_stripname '-L' '' "$deplib" + newlib_search_path="$newlib_search_path $func_stripname_result" + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + func_stripname '-L' '' "$deplib" + newlib_search_path="$newlib_search_path $func_stripname_result" + ;; + *) + func_warning "\`-L' is ignored for archives/objects" + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + func_stripname '-R' '' "$deplib" + dir=$func_stripname_result + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) lib="$deplib" ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + # Linking convenience modules into shared libraries is allowed, + # but linking other static libraries is non-portable. + case " $dlpreconveniencelibs " in + *" $deplib "*) ;; + *) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + if eval "\$ECHO \"X$deplib\"" 2>/dev/null | $Xsed -e 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=yes + fi + ;; + pass_all) + valid_a_lib=yes + ;; + esac + if test "$valid_a_lib" != yes; then + $ECHO + $ECHO "*** Warning: Trying to link with static lib archive $deplib." + $ECHO "*** I have the capability to make that library automatically link in when" + $ECHO "*** you link to this library. But I can only do this if you have a" + $ECHO "*** shared version of the library, which you do not appear to have" + $ECHO "*** because the file extensions .$libext of this argument makes me believe" + $ECHO "*** that it is just a static archive that I should not use here." + else + $ECHO + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + ;; + esac + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + newdlprefiles="$newdlprefiles $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + newdlfiles="$newdlfiles $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + + if test "$found" = yes || test -f "$lib"; then : + else + func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" + fi + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$lib" \ + || func_fatal_error "\`$lib' is not a valid libtool archive" + + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + inherited_linker_flags= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + func_source "$lib" + + # Convert "-framework foo" to "foo.ltframework" + if test -n "$inherited_linker_flags"; then + tmp_inherited_linker_flags=`$ECHO "X$inherited_linker_flags" | $Xsed -e 's/-framework \([^ $]*\)/\1.ltframework/g'` + for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do + case " $new_inherited_linker_flags " in + *" $tmp_inherited_linker_flag "*) ;; + *) new_inherited_linker_flags="$new_inherited_linker_flags $tmp_inherited_linker_flag";; + esac + done + fi + dependency_libs=`$ECHO "X $dependency_libs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && dlfiles="$dlfiles $dlopen" + test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + # It is a libtool convenience library, so add in its objects. + convenience="$convenience $ladir/$objdir/$old_library" + old_convenience="$old_convenience $ladir/$objdir/$old_library" + elif test "$linkmode" != prog && test "$linkmode" != lib; then + func_fatal_error "\`$lib' is not a convenience library" + fi + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if $opt_duplicate_deps ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + for l in $old_library $library_names; do + linklib="$l" + done + if test -z "$linklib"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + func_fatal_error "cannot -dlopen a convenience library: \`$lib'" + fi + if test -z "$dlname" || + test "$dlopen_support" != yes || + test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + dlprefiles="$dlprefiles $lib $dependency_libs" + else + newdlfiles="$newdlfiles $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + func_warning "cannot determine absolute directory name of \`$ladir'" + func_warning "passing it literally to the linker, although it might fail" + abs_ladir="$ladir" + fi + ;; + esac + func_basename "$lib" + laname="$func_basename_result" + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + func_warning "library \`$lib' was moved." + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$libdir" + absdir="$libdir" + fi + test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir="$ladir" + absdir="$abs_ladir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + fi + fi # $installed = yes + func_stripname 'lib' '.la' "$laname" + name=$func_stripname_result + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir" && test "$linkmode" = prog; then + func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" + fi + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + newdlprefiles="$newdlprefiles $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + dlpreconveniencelibs="$dlpreconveniencelibs $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + newdlprefiles="$newdlprefiles $dir/$dlname" + else + newdlprefiles="$newdlprefiles $dir/$linklib" + fi + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + newlib_search_path="$newlib_search_path $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + newlib_search_path="$newlib_search_path $func_stripname_result" + ;; + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if $opt_duplicate_deps ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { { test "$prefer_static_libs" = no || + test "$prefer_static_libs,$installed" = "built,yes"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath:" in + *"$absdir:"*) ;; + *) temp_rpath="$temp_rpath$absdir:" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test "$use_static_libs" = built && test "$installed" = yes; then + use_static_libs=no + fi + if test -n "$library_names" && + { test "$use_static_libs" = no || test -z "$old_library"; }; then + case $host in + *cygwin* | *mingw* | *cegcc*) + # No point in relinking DLLs because paths are not encoded + notinst_deplibs="$notinst_deplibs $lib" + need_relink=no + ;; + *) + if test "$installed" = no; then + notinst_deplibs="$notinst_deplibs $lib" + need_relink=yes + fi + ;; + esac + # This is a shared library + + # Warn about portability, can't link against -module's on some + # systems (darwin). Don't bleat about dlopened modules though! + dlopenmodule="" + for dlpremoduletest in $dlprefiles; do + if test "X$dlpremoduletest" = "X$lib"; then + dlopenmodule="$dlpremoduletest" + break + fi + done + if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then + $ECHO + if test "$linkmode" = prog; then + $ECHO "*** Warning: Linking the executable $output against the loadable module" + else + $ECHO "*** Warning: Linking the shared library $output against the loadable module" + fi + $ECHO "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + shift + realname="$1" + shift + libname=`eval "\\$ECHO \"$libname_spec\""` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw* | *cegcc*) + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + func_basename "$soroot" + soname="$func_basename_result" + func_stripname 'lib' '.dll' "$soname" + newlib=libimp-$func_stripname_result.a + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + func_verbose "extracting exported symbol list from \`$soname'" + func_execute_cmds "$extract_expsyms_cmds" 'exit $?' + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + func_verbose "generating import library for \`$soname'" + func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; + *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a (non-dlopened) module then we can not + # link against it, someone is ignoring the earlier warnings + if /usr/bin/file -L $add 2> /dev/null | + $GREP ": [^:]* bundle" >/dev/null ; then + if test "X$dlopenmodule" != "X$lib"; then + $ECHO "*** Warning: lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + $ECHO + $ECHO "*** And there doesn't seem to be a static archive available" + $ECHO "*** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + elif test -n "$old_library"; then + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$dir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + func_fatal_configuration "unsupported hardcode properties" + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && + test "$hardcode_minus_L" != yes && + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + $ECHO + $ECHO "*** Warning: This system can not link to static lib archive $lib." + $ECHO "*** I have the capability to make that library automatically link in when" + $ECHO "*** you link to this library. But I can only do this if you have a" + $ECHO "*** shared version of the library, which you do not appear to have." + if test "$module" = yes; then + $ECHO "*** But as you try to build a module library, libtool will still create " + $ECHO "*** a static module, that should work as long as the dlopening application" + $ECHO "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + $ECHO + $ECHO "*** However, this would only work if libtool was able to extract symbol" + $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could" + $ECHO "*** not find such a program. So, this module is probably useless." + $ECHO "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test "$linkmode" = lib; then + if test -n "$dependency_libs" && + { test "$hardcode_into_libs" != yes || + test "$build_old_libs" = yes || + test "$link_static" = yes; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) func_stripname '-R' '' "$libdir" + temp_xrpath=$func_stripname_result + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) xrpath="$xrpath $temp_xrpath";; + esac;; + *) temp_deplibs="$temp_deplibs $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + newlib_search_path="$newlib_search_path $absdir" + # Link against this library + test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + if $opt_duplicate_deps ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + case $deplib in + -L*) path="$deplib" ;; + *.la) + func_dirname "$deplib" "" "." + dir="$func_dirname_result" + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + func_warning "cannot determine absolute directory name of \`$dir'" + absdir="$dir" + fi + ;; + esac + if $GREP "^installed=no" $deplib > /dev/null; then + case $host in + *-*-darwin*) + depdepl= + eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names" ; then + for tmp in $deplibrary_names ; do + depdepl=$tmp + done + if test -f "$absdir/$objdir/$depdepl" ; then + depdepl="$absdir/$objdir/$depdepl" + darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -z "$darwin_install_name"; then + darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + fi + compiler_flags="$compiler_flags ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" + linker_flags="$linker_flags -dylib_file ${darwin_install_name}:${depdepl}" + path= + fi + fi + ;; + *) + path="-L$absdir/$objdir" + ;; + esac + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + test "$absdir" != "$libdir" && \ + func_warning "\`$deplib' seems to be moved" + + path="-L$absdir" + fi + ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + if test "$pass" = link; then + if test "$linkmode" = "prog"; then + compile_deplibs="$new_inherited_linker_flags $compile_deplibs" + finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" + else + compiler_flags="$compiler_flags "`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + fi + fi + dependency_libs="$newdependency_libs" + if test "$pass" = dlpreopen; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test "$pass" != dlopen; then + if test "$pass" != conv; then + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) lib_search_path="$lib_search_path $dir" ;; + esac + done + newlib_search_path= + fi + + if test "$linkmode,$pass" != "prog,link"; then + vars="deplibs" + else + vars="compile_deplibs finalize_deplibs" + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs ; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i="" + ;; + esac + if test -n "$i" ; then + tmp_libs="$tmp_libs $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + fi + if test "$linkmode" = prog || test "$linkmode" = lib; then + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for archives" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for archives" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for archives" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for archives" + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for archives" + + test -n "$release" && \ + func_warning "\`-release' is ignored for archives" + + test -n "$export_symbols$export_symbols_regex" && \ + func_warning "\`-export-symbols' is ignored for archives" + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + objs="$objs$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + func_stripname 'lib' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + test "$module" = no && \ + func_fatal_help "libtool library \`$output' must begin with \`lib'" + + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + func_stripname '' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + func_stripname '' '.la' "$outputname" + libname=$func_stripname_result + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" + else + $ECHO + $ECHO "*** Warning: Linking the shared library $output against the non-libtool" + $ECHO "*** objects $objs is not portable!" + libobjs="$libobjs $objs" + fi + fi + + test "$dlself" != no && \ + func_warning "\`-dlopen self' is ignored for libtool libraries" + + set dummy $rpath + shift + test "$#" -gt 1 && \ + func_warning "ignoring multiple \`-rpath's for a libtool library" + + install_libdir="$1" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + # Some compilers have problems with a `.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for convenience libraries" + + test -n "$release" && \ + func_warning "\`-release' is ignored for convenience libraries" + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + shift + IFS="$save_ifs" + + test -n "$7" && \ + func_fatal_help "too many parameters to \`-version-info'" + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major="$1" + number_minor="$2" + number_revision="$3" + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # which has an extra 1 added just for fun + # + case $version_type in + darwin|linux|osf|windows|none) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_minor" + lt_irix_increment=no + ;; + *) + func_fatal_configuration "$modename: unknown library version type \`$version_type'" + ;; + esac + ;; + no) + current="$1" + revision="$2" + age="$3" + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "CURRENT \`$current' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "REVISION \`$revision' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "AGE \`$age' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + if test "$age" -gt "$current"; then + func_error "AGE \`$age' is greater than the current interface number \`$current'" + func_fatal_error "\`$vinfo' is not valid version information" + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + func_arith $current + 1 + minor_current=$func_arith_result + xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current" + ;; + + irix | nonstopux) + if test "X$lt_irix_increment" = "Xno"; then + func_arith $current - $age + else + func_arith $current - $age + 1 + fi + major=$func_arith_result + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring="$verstring_prefix$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test "$loop" -ne 0; do + func_arith $revision - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + ;; + + osf) + func_arith $current - $age + major=.$func_arith_result + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test "$loop" -ne 0; do + func_arith $current - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + verstring="$verstring:${current}.0" + ;; + + qnx) + major=".$current" + versuffix=".$current" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + + *) + func_fatal_configuration "unknown library version type \`$version_type'" + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring="0.0" + ;; + esac + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + func_warning "undefined symbols not allowed in $host shared libraries" + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + + fi + + func_generate_dlsyms "$libname" "$libname" "yes" + libobjs="$libobjs $symfileobj" + test "X$libobjs" = "X " && libobjs= + + if test "$mode" != relink; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$ECHO "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext | *.gcno) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + if test "X$precious_files_regex" != "X"; then + if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + removelist="$removelist $p" + ;; + *) ;; + esac + done + test -n "$removelist" && \ + func_show_eval "${RM}r \$removelist" + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + oldlibs="$oldlibs $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + #for path in $notinst_path; do + # lib_search_path=`$ECHO "X$lib_search_path " | $Xsed -e "s% $path % %g"` + # deplibs=`$ECHO "X$deplibs " | $Xsed -e "s% -L$path % %g"` + # dependency_libs=`$ECHO "X$dependency_libs " | $Xsed -e "s% -L$path % %g"` + #done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + temp_xrpath="$temp_xrpath -R$libdir" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles="$dlfiles" + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) dlfiles="$dlfiles $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles="$dlprefiles" + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) dlprefiles="$dlprefiles $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + deplibs="$deplibs System.ltframework" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + deplibs="$deplibs -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $opt_dry_run || $RM conftest.c + cat > conftest.c </dev/null` + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null | + $GREP " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$ECHO "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | + $SED -e 10q | + $EGREP "$file_magic_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $ECHO + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + $ECHO "*** I have the capability to make that library automatically link in when" + $ECHO "*** you link to this library. But I can only do this if you have a" + $ECHO "*** shared version of the library, which you do not appear to have" + $ECHO "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for file magic test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a file magic. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib="$potent_lib" # see symlink-check above in file_magic test + if eval "\$ECHO \"X$potent_lib\"" 2>/dev/null | $Xsed -e 10q | \ + $EGREP "$match_pattern_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $ECHO + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + $ECHO "*** I have the capability to make that library automatically link in when" + $ECHO "*** you link to this library. But I can only do this if you have a" + $ECHO "*** shared version of the library, which you do not appear to have" + $ECHO "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a regex pattern. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$ECHO "X $deplibs" | $Xsed \ + -e 's/ -lc$//' -e 's/ -[LR][^ ]*//g'` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + for i in $predeps $postdeps ; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$ECHO "X $tmp_deplibs" | $Xsed -e "s,$i,,"` + done + fi + if $ECHO "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' | + $GREP . >/dev/null; then + $ECHO + if test "X$deplibs_check_method" = "Xnone"; then + $ECHO "*** Warning: inter-library dependencies are not supported in this platform." + else + $ECHO "*** Warning: inter-library dependencies are not known to be supported." + fi + $ECHO "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + fi + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library with the System framework + newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's/ -lc / System.ltframework /'` + ;; + esac + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + $ECHO + $ECHO "*** Warning: libtool could not satisfy all declared inter-library" + $ECHO "*** dependencies of module $libname. Therefore, libtool will create" + $ECHO "*** a static module, that should work as long as the dlopening" + $ECHO "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + $ECHO + $ECHO "*** However, this would only work if libtool was able to extract symbol" + $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could" + $ECHO "*** not find such a program. So, this module is probably useless." + $ECHO "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + $ECHO "*** The inter-library dependencies that have been dropped here will be" + $ECHO "*** automatically added whenever a program is linked with this library" + $ECHO "*** or is declared to -dlopen it." + + if test "$allow_undefined" = no; then + $ECHO + $ECHO "*** Since this library must not contain undefined symbols," + $ECHO "*** because either the platform does not support them or" + $ECHO "*** it was explicitly requested with -no-undefined," + $ECHO "*** libtool will only create a static version of it." + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + case $host in + *-*-darwin*) + newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + new_inherited_linker_flags=`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + deplibs=`$ECHO "X $deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + new_libs="$new_libs -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$new_libs $deplib" ;; + esac + ;; + *) new_libs="$new_libs $deplib" ;; + esac + done + deplibs="$new_libs" + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$mode" != relink && rpath="$compile_rpath$rpath" + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + dep_rpath="$dep_rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + if test -n "$hardcode_libdir_flag_spec_ld"; then + eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" + else + eval dep_rpath=\"$hardcode_libdir_flag_spec\" + fi + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath="$finalize_shlibpath" + test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + shift + realname="$1" + shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib="$output_objdir/$realname" + linknames= + for link + do + linknames="$linknames $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$ECHO "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + test "X$libobjs" = "X " && libobjs= + + delfiles= + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" + export_symbols="$output_objdir/$libname.uexp" + delfiles="$delfiles $export_symbols" + fi + + orig_export_symbols= + case $host_os in + cygwin* | mingw* | cegcc*) + if test -n "$export_symbols" && test -z "$export_symbols_regex"; then + # exporting using user supplied symfile + if test "x`$SED 1q $export_symbols`" != xEXPORTS; then + # and it's NOT already a .def file. Must figure out + # which of the given symbols are data symbols and tag + # them as such. So, trigger use of export_symbols_cmds. + # export_symbols gets reassigned inside the "prepare + # the list of exported symbols" if statement, so the + # include_expsyms logic still works. + orig_export_symbols="$export_symbols" + export_symbols= + always_export_symbols=yes + fi + fi + ;; + esac + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + func_len " $cmd" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + func_show_eval "$cmd" 'exit $?' + skipped_export=false + else + # The command line is too long to execute in one step. + func_verbose "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"' + fi + + if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + delfiles="$delfiles $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + tmp_deplibs="$tmp_deplibs $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec" && + test "$compiler_needs_object" = yes && + test -z "$libobjs"; then + # extract the archives, so we have objects to list. + # TODO: could optimize this to just extract one archive. + whole_archive_flag_spec= + fi + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + else + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $convenience + libobjs="$libobjs $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + linker_flags="$linker_flags $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test "X$skipped_export" != "X:" && + func_len " $test_cmds" && + len=$func_len_result && + test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise + # or, if using GNU ld and skipped_export is not :, use a linker + # script. + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + output_la=`$ECHO "X$output" | $Xsed -e "$basename"` + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + last_robj= + k=1 + + if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then + output=${output_objdir}/${output_la}.lnkscript + func_verbose "creating GNU ld script: $output" + $ECHO 'INPUT (' > $output + for obj in $save_libobjs + do + $ECHO "$obj" >> $output + done + $ECHO ')' >> $output + delfiles="$delfiles $output" + elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then + output=${output_objdir}/${output_la}.lnk + func_verbose "creating linker input file list: $output" + : > $output + set x $save_libobjs + shift + firstobj= + if test "$compiler_needs_object" = yes; then + firstobj="$1 " + shift + fi + for obj + do + $ECHO "$obj" >> $output + done + delfiles="$delfiles $output" + output=$firstobj\"$file_list_spec$output\" + else + if test -n "$save_libobjs"; then + func_verbose "creating reloadable object files..." + output=$output_objdir/$output_la-${k}.$objext + eval test_cmds=\"$reload_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + if test "X$objlist" = X || + test "$len" -lt "$max_cmd_len"; then + func_append objlist " $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test "$k" -eq 1 ; then + # The first file doesn't have a previous command to add. + eval concat_cmds=\"$reload_cmds $objlist $last_robj\" + else + # All subsequent reloadable object files will link in + # the last one created. + eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj~\$RM $last_robj\" + fi + last_robj=$output_objdir/$output_la-${k}.$objext + func_arith $k + 1 + k=$func_arith_result + output=$output_objdir/$output_la-${k}.$objext + objlist=$obj + func_len " $last_robj" + func_arith $len0 + $func_len_result + len=$func_arith_result + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" + if test -n "$last_robj"; then + eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" + fi + delfiles="$delfiles $output" + + else + output= + fi + + if ${skipped_export-false}; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + libobjs=$output + # Append the command to create the export file. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + fi + + test -n "$save_libobjs" && + func_verbose "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs="$IFS"; IFS='~' + for cmd in $concat_cmds; do + IFS="$save_ifs" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + if test -n "$export_symbols_regex" && ${skipped_export-false}; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + + if ${skipped_export-false}; then + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"' + fi + + if test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + delfiles="$delfiles $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + fi + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + fi + + if test -n "$delfiles"; then + # Append the command to remove temporary files to $cmds. + eval cmds=\"\$cmds~\$RM $delfiles\" + fi + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $dlprefiles + libobjs="$libobjs $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + func_show_eval '${RM}r "$gentop"' + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + ;; + + obj) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for objects" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for objects" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for objects" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for objects" + + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for objects" + + test -n "$release" && \ + func_warning "\`-release' is ignored for objects" + + case $output in + *.lo) + test -n "$objs$old_deplibs" && \ + func_fatal_error "cannot build library object \`$output' from non-libtool objects" + + libobj=$output + func_lo2o "$libobj" + obj=$func_lo2o_result + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $opt_dry_run || $RM $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec and hope we can get by with + # turning comma into space.. + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + reload_conv_objs=$reload_objs\ `$ECHO "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'` + else + gentop="$output_objdir/${obj}x" + generated="$generated $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + func_execute_cmds "$reload_cmds" 'exit $?' + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + fi + + if test -n "$pic_flag" || test "$pic_mode" != default; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + func_execute_cmds "$reload_cmds" 'exit $?' + fi + + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) func_stripname '' '.exe' "$output" + output=$func_stripname_result.exe;; + esac + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for programs" + + test -n "$release" && \ + func_warning "\`-release' is ignored for programs" + + test "$preload" = yes \ + && test "$dlopen_support" = unknown \ + && test "$dlopen_self" = unknown \ + && test "$dlopen_self_static" = unknown && \ + func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'` + finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'` + ;; + esac + + case $host in + *-*-darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + # But is supposedly fixed on 10.4 or later (yay!). + if test "$tagname" = CXX ; then + case ${MACOSX_DEPLOYMENT_TARGET-10.0} in + 10.[0123]) + compile_command="$compile_command ${wl}-bind_at_load" + finalize_command="$finalize_command ${wl}-bind_at_load" + ;; + esac + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + new_libs="$new_libs -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$new_libs $deplib" ;; + esac + ;; + *) new_libs="$new_libs $deplib" ;; + esac + done + compile_deplibs="$new_libs" + + + compile_command="$compile_command $compile_deplibs" + finalize_command="$finalize_command $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + ::) dllsearchpath=$libdir;; + *) dllsearchpath="$dllsearchpath:$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) dllsearchpath="$dllsearchpath:$testbindir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$ECHO "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + finalize_command=`$ECHO "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + fi + + func_generate_dlsyms "$outputname" "@PROGRAM@" "no" + + # template prelinking step + if test -n "$prelink_cmds"; then + func_execute_cmds "$prelink_cmds" 'exit $?' + fi + + wrappers_required=yes + case $host in + *cygwin* | *mingw* ) + if test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + *cegcc) + # Disable wrappers for cegcc, we are cross compiling anyway. + wrappers_required=no + ;; + *) + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + esac + if test "$wrappers_required" = no; then + # Replace the output file specification. + compile_command=`$ECHO "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + exit_status=0 + func_show_eval "$link_command" 'exit_status=$?' + + # Delete the generated files. + if test -f "$output_objdir/${outputname}S.${objext}"; then + func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' + fi + + exit $exit_status + fi + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + rpath="$rpath$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test "$no_install" = yes; then + # We don't need to create a wrapper script. + link_command="$compile_var$compile_command$compile_rpath" + # Replace the output file specification. + link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $opt_dry_run || $RM $output + # Link the executable and exit + func_show_eval "$link_command" 'exit $?' + exit $EXIT_SUCCESS + fi + + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + func_warning "this platform does not like uninstalled shared libraries" + func_warning "\`$output' will be relinked during installation" + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$ECHO "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` + else + # fast_install is set to needless + relink_command= + fi + else + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + fi + fi + + # Replace the output file specification. + link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname + + func_show_eval "$link_command" 'exit $?' + + # Now create the wrapper script. + func_verbose "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"` + fi + + # Quote $ECHO for shipping. + if test "X$ECHO" = "X$SHELL $progpath --fallback-echo"; then + case $progpath in + [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";; + *) qecho="$SHELL `pwd`/$progpath --fallback-echo";; + esac + qecho=`$ECHO "X$qecho" | $Xsed -e "$sed_quote_subst"` + else + qecho=`$ECHO "X$ECHO" | $Xsed -e "$sed_quote_subst"` + fi + + # Only actually do things if not in dry run mode. + $opt_dry_run || { + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) func_stripname '' '.exe' "$output" + output=$func_stripname_result ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + func_stripname '' '.exe' "$outputname" + outputname=$func_stripname_result ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + func_dirname_and_basename "$output" "" "." + output_name=$func_basename_result + output_path=$func_dirname_result + cwrappersource="$output_path/$objdir/lt-$output_name.c" + cwrapper="$output_path/$output_name.exe" + $RM $cwrappersource $cwrapper + trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + func_emit_cwrapperexe_src > $cwrappersource + + # The wrapper executable is built using the $host compiler, + # because it contains $host paths and files. If cross- + # compiling, it, like the target executable, must be + # executed on the $host or under an emulation environment. + $opt_dry_run || { + $LTCC $LTCFLAGS -o $cwrapper $cwrappersource + $STRIP $cwrapper + } + + # Now, create the wrapper script for func_source use: + func_ltwrapper_scriptname $cwrapper + $RM $func_ltwrapper_scriptname_result + trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 + $opt_dry_run || { + # note: this script will not be executed, so do not chmod. + if test "x$build" = "x$host" ; then + $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result + else + func_emit_wrapper no > $func_ltwrapper_scriptname_result + fi + } + ;; + * ) + $RM $output + trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 + + func_emit_wrapper no > $output + chmod +x $output + ;; + esac + } + exit $EXIT_SUCCESS + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save $symfileobj" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$old_deplibs $non_pic_objects" + if test "$preload" = yes && test -f "$symfileobj"; then + oldobjs="$oldobjs $symfileobj" + fi + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $addlibs + oldobjs="$oldobjs $func_extract_archives_result" + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + cmds=$old_archive_from_new_cmds + else + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $dlprefiles + oldobjs="$oldobjs $func_extract_archives_result" + fi + + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + func_basename "$obj" + $ECHO "$func_basename_result" + done | sort | sort -uc >/dev/null 2>&1); then + : + else + $ECHO "copying selected object files to avoid basename conflicts..." + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + func_mkdir_p "$gentop" + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + func_basename "$obj" + objbase="$func_basename_result" + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + func_arith $counter + 1 + counter=$func_arith_result + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + oldobjs="$oldobjs $gentop/$newobj" + ;; + *) oldobjs="$oldobjs $obj" ;; + esac + done + fi + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + func_verbose "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + oldobjs= + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + eval test_cmds=\"$old_archive_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + for obj in $save_oldobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + func_append objlist " $obj" + if test "$len" -lt "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj" ; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + objlist= + len=$len0 + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test "X$oldobjs" = "X" ; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + func_execute_cmds "$cmds" 'exit $?' + done + + test -n "$generated" && \ + func_show_eval "${RM}r$generated" + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + func_verbose "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi + + # Only create the output if not a dry run. + $opt_dry_run || { + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + func_basename "$deplib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + newdependency_libs="$newdependency_libs $libdir/$name" + ;; + *) newdependency_libs="$newdependency_libs $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + + for lib in $dlfiles; do + case $lib in + *.la) + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + newdlfiles="$newdlfiles $libdir/$name" + ;; + *) newdlfiles="$newdlfiles $lib" ;; + esac + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + *.la) + # Only pass preopened files to the pseudo-archive (for + # eventual linking with the app. that links it) if we + # didn't already link the preopened objects directly into + # the library: + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + newdlprefiles="$newdlprefiles $libdir/$name" + ;; + esac + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlfiles="$newdlfiles $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlprefiles="$newdlprefiles $abs" + done + dlprefiles="$newdlprefiles" + fi + $RM $output + # place dlname in correct position for cygwin + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; + esac + $ECHO > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='$new_inherited_linker_flags' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Names of additional weak libraries provided by this library +weak_library_names='$weak_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test "$installed" = no && test "$need_relink" = yes; then + $ECHO >> $output "\ +relink_command=\"$relink_command\"" + fi + done + } + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' + ;; + esac + exit $EXIT_SUCCESS +} + +{ test "$mode" = link || test "$mode" = relink; } && + func_mode_link ${1+"$@"} + + +# func_mode_uninstall arg... +func_mode_uninstall () +{ + $opt_debug + RM="$nonopt" + files= + rmforce= + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + for arg + do + case $arg in + -f) RM="$RM $arg"; rmforce=yes ;; + -*) RM="$RM $arg" ;; + *) files="$files $arg" ;; + esac + done + + test -z "$RM" && \ + func_fatal_help "you must specify an RM program" + + rmdirs= + + origobjdir="$objdir" + for file in $files; do + func_dirname "$file" "" "." + dir="$func_dirname_result" + if test "X$dir" = X.; then + objdir="$origobjdir" + else + objdir="$dir/$origobjdir" + fi + func_basename "$file" + name="$func_basename_result" + test "$mode" = uninstall && objdir="$dir" + + # Remember objdir for removal later, being careful to avoid duplicates + if test "$mode" = clean; then + case " $rmdirs " in + *" $objdir "*) ;; + *) rmdirs="$rmdirs $objdir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if { test -L "$file"; } >/dev/null 2>&1 || + { test -h "$file"; } >/dev/null 2>&1 || + test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif test "$rmforce" = yes; then + continue + fi + + rmfiles="$file" + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if func_lalib_p "$file"; then + func_source $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + rmfiles="$rmfiles $objdir/$n" + done + test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library" + + case "$mode" in + clean) + case " $library_names " in + # " " in the beginning catches empty $dlname + *" $dlname "*) ;; + *) rmfiles="$rmfiles $objdir/$dlname" ;; + esac + test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if func_lalib_p "$file"; then + + # Read the .lo file + func_source $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" && + test "$pic_object" != none; then + rmfiles="$rmfiles $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" && + test "$non_pic_object" != none; then + rmfiles="$rmfiles $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$mode" = clean ; then + noexename=$name + case $file in + *.exe) + func_stripname '' '.exe' "$file" + file=$func_stripname_result + func_stripname '' '.exe' "$name" + noexename=$func_stripname_result + # $file with .exe has already been added to rmfiles, + # add $file without .exe + rmfiles="$rmfiles $file" + ;; + esac + # Do a test to see if this is a libtool program. + if func_ltwrapper_p "$file"; then + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + relink_command= + func_source $func_ltwrapper_scriptname_result + rmfiles="$rmfiles $func_ltwrapper_scriptname_result" + else + relink_command= + func_source $dir/$noexename + fi + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + rmfiles="$rmfiles $objdir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + rmfiles="$rmfiles $objdir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + func_show_eval "$RM $rmfiles" 'exit_status=1' + done + objdir="$origobjdir" + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + func_show_eval "rmdir $dir >/dev/null 2>&1" + fi + done + + exit $exit_status +} + +{ test "$mode" = uninstall || test "$mode" = clean; } && + func_mode_uninstall ${1+"$@"} + +test -z "$mode" && { + help="$generic_help" + func_fatal_help "you must specify a MODE" +} + +test -z "$exec_cmd" && \ + func_fatal_help "invalid operation mode \`$mode'" + +if test -n "$exec_cmd"; then + eval exec "$exec_cmd" + exit $EXIT_FAILURE +fi + +exit $exit_status + + +# The TAGs below are defined such that we never get into a situation +# in which we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +build_libtool_libs=no +build_old_libs=yes +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: +# vi:sw=2 + diff --git a/2.5.13/2.5.x/apache2/configure b/2.5.13/2.5.x/apache2/configure new file mode 100755 index 00000000..a58eaf34 --- /dev/null +++ b/2.5.13/2.5.x/apache2/configure @@ -0,0 +1,6564 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.64. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software +# Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + # We cannot yet assume a decent shell, so we have to provide a + # neutralization value for shells without unset; and this also + # works around shells that cannot unset nonexistent variables. + BASH_ENV=/dev/null + ENV=/dev/null + (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error ERROR [LINENO LOG_FD] +# --------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with status $?, using 1 if that was 0. +as_fn_error () +{ + as_status=$?; test $as_status -eq 0 && as_status=1 + if test "$3"; then + as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 + fi + $as_echo "$as_me: error: $1" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= +PACKAGE_URL= + +ac_unique_file="mod_security2.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +CURL_USES_GNUTLS +CURL_CFLAGS +CURL_LIBS +LUA_CFLAGS +LUA_LIBS +LIBXML2_CFLAGS +LIBXML2_LIBS +APU_LINK_LD +APU_LDFLAGS +APU_CFLAGS +APU_LIBS +APR_LINK_LD +APR_LDFLAGS +APR_CFLAGS +APR_LIBS +PCRE_CFLAGS +PCRE_LIBS +APXS_HTTPD +APXS_LIBEXECDIR +APXS_PROGNAME +APXS_SBINDIR +APXS_BINDIR +APXS_LIBDIR +APXS_CC +APXS_LIBTOOL +APXS_CFLAGS +APXS_LIBS +APXS_LDFLAGS +MODSEC_APXS_EXTRA_CFLAGS +APXS_EXTRA_CFLAGS +APXS_INCLUDES +APXS_INCLUDEDIR +APXS_WRAPPER +APXS +MODSEC_EXTRA_CFLAGS +EXTRA_CFLAGS +MSC_REGRESSION_DOCROOT_DIR +MSC_REGRESSION_LOGS_DIR +MSC_REGRESSION_CONF_DIR +MSC_REGRESSION_SERVERROOT_DIR +MSC_REGRESSION_DIR +MSC_TEST_DIR +MSC_PKGBASE_DIR +MSC_BASE_DIR +LIBOBJS +EGREP +ENV_CMD +PERL +GREP +RANLIB +SET_MAKE +LN_S +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +AWK +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_pcre_study +enable_pcre_match_limit +enable_pcre_match_limit_recursion +enable_errors +enable_verbose_output +enable_strict_compile +enable_debug_conf +enable_debug_cache +enable_debug_acmp +enable_debug_mem +enable_performance_measurement +enable_modsec_api +with_apxs +with_pcre +with_apr +with_apu +with_libxml +with_lua +with_curl +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information." + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-pcre-study Enable PCRE regex studying during configure. + --enable-pcre-match-limit + Enable PCRE regex match limit during configure. + --enable-pcre-match-limit-recursion + Enable PCRE regex match limit recursion during + configure. + --disable-errors Disable errors during configure. + --enable-verbose-output Enable more verbose configure output. + --enable-strict-compile Enable strict compilation (warnings are errors). + --enable-debug-conf Enable debug during configuration. + --enable-debug-cache Enable debug for transformation caching. + --enable-debug-acmp Enable debugging acmp code. + --enable-debug-mem Enable debug during configuration. + --enable-performance-measurement + Enable performance-measurement stats. + --disable-modsec-api Disable the API; compiling against some older Apache + versions require this. + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-apxs=FILE FILE is the path to apxs; defaults to "apxs". + --with-pcre=PATH Path to pcre prefix or config script + --with-apr=PATH Path to apr prefix or config script + --with-apu=PATH Path to apu prefix or config script + --with-libxml=PATH Path to libxml2 prefix or config script + --with-lua=PATH Path to lua prefix or config script + --with-curl=PATH Path to curl prefix or config script + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.64 + +Copyright (C) 2009 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + return $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + return $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + return $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_header_compile + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_type + +# ac_fn_c_find_uintX_t LINENO BITS VAR +# ------------------------------------ +# Finds an unsigned integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_uintX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 +$as_echo_n "checking for uint$2_t... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ + 'unsigned long long int' 'unsigned short int' 'unsigned char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(($ac_type) -1 >> ($2 - 1) == 1)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + case $ac_type in #( + uint$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + eval as_val=\$$3 + if test "x$as_val" = x""no; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_find_uintX_t + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + return $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_func +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.64. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + ac_site_file1=$CONFIG_SITE +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_config_headers="$ac_config_headers mod_security2_config.h" + +ac_aux_dir= +for ac_dir in build "$srcdir"/build; do + for ac_t in install-sh install.sh shtool; do + if test -f "$ac_dir/$ac_t"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/$ac_t -c" + break 2 + fi + done +done +if test -z "$ac_aux_dir"; then + as_fn_error "cannot find install-sh, install.sh, or shtool in build \"$srcdir\"/build" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + + +# Checks for programs. +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_AWK+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "no acceptable C compiler found in \$PATH +See \`config.log' for more details." "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + rm -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out conftest.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +if test -z "$ac_file"; then : + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ as_fn_set_status 77 +as_fn_error "C compiler cannot create executables +See \`config.log' for more details." "$LINENO" 5; }; } +fi +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out conftest.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." "$LINENO" 5; } +fi +rm -f conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if test "${ac_cv_objext+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "cannot compute suffix of object files: cannot compile +See \`config.log' for more details." "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_RANLIB+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if test "${ac_cv_path_GREP+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +for ac_prog in perl perl5 +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_path_PERL+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + case $PERL in + [\\/]* | ?:[\\/]*) + ac_cv_path_PERL="$PERL" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_PERL="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PERL=$ac_cv_path_PERL +if test -n "$PERL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5 +$as_echo "$PERL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$PERL" && break +done + +for ac_prog in env printenv +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_path_ENV_CMD+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + case $ENV_CMD in + [\\/]* | ?:[\\/]*) + ac_cv_path_ENV_CMD="$ENV_CMD" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_ENV_CMD="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ENV_CMD=$ac_cv_path_ENV_CMD +if test -n "$ENV_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENV_CMD" >&5 +$as_echo "$ENV_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ENV_CMD" && break +done + + +# Checks for header files. + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in fcntl.h limits.h stdlib.h string.h unistd.h sys/types.h sys/stat.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Checks for typedefs, structures, and compiler characteristics. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 +$as_echo_n "checking for an ANSI C-conforming const... " >&6; } +if test "${ac_cv_c_const+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset cs; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_const=yes +else + ac_cv_c_const=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 +$as_echo "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +$as_echo "#define const /**/" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if test "${ac_cv_c_inline+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C/C++ restrict keyword" >&5 +$as_echo_n "checking for C/C++ restrict keyword... " >&6; } +if test "${ac_cv_c_restrict+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_restrict=no + # The order here caters to the fact that C++ does not require restrict. + for ac_kw in __restrict __restrict__ _Restrict restrict; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +typedef int * int_ptr; + int foo (int_ptr $ac_kw ip) { + return ip[0]; + } +int +main () +{ +int s[1]; + int * $ac_kw t = s; + t[0] = 0; + return foo(t) + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_restrict=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_restrict" != no && break + done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_restrict" >&5 +$as_echo "$ac_cv_c_restrict" >&6; } + + case $ac_cv_c_restrict in + restrict) ;; + no) $as_echo "#define restrict /**/" >>confdefs.h + ;; + *) cat >>confdefs.h <<_ACEOF +#define restrict $ac_cv_c_restrict +_ACEOF + ;; + esac + +ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" +if test "x$ac_cv_type_pid_t" = x""yes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = x""yes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 +$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } +if test "${ac_cv_struct_tm+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include + +int +main () +{ +struct tm tm; + int *p = &tm.tm_sec; + return !p; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_struct_tm=time.h +else + ac_cv_struct_tm=sys/time.h +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5 +$as_echo "$ac_cv_struct_tm" >&6; } +if test $ac_cv_struct_tm = sys/time.h; then + +$as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h + +fi + +ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" +case $ac_cv_c_uint8_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT8_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint8_t $ac_cv_c_uint8_t +_ACEOF +;; + esac + + +# Checks for library functions. +for ac_header in stdlib.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" +if test "x$ac_cv_header_stdlib_h" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STDLIB_H 1 +_ACEOF + +fi + +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5 +$as_echo_n "checking for GNU libc compatible malloc... " >&6; } +if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_malloc_0_nonnull=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#if defined STDC_HEADERS || defined HAVE_STDLIB_H +# include +#else +char *malloc (); +#endif + +int +main () +{ +return ! malloc (0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_malloc_0_nonnull=yes +else + ac_cv_func_malloc_0_nonnull=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_malloc_0_nonnull" >&5 +$as_echo "$ac_cv_func_malloc_0_nonnull" >&6; } +if test $ac_cv_func_malloc_0_nonnull = yes; then : + +$as_echo "#define HAVE_MALLOC 1" >>confdefs.h + +else + $as_echo "#define HAVE_MALLOC 0" >>confdefs.h + + case " $LIBOBJS " in + *" malloc.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS malloc.$ac_objext" + ;; +esac + + +$as_echo "#define malloc rpl_malloc" >>confdefs.h + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working memcmp" >&5 +$as_echo_n "checking for working memcmp... " >&6; } +if test "${ac_cv_func_memcmp_working+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_memcmp_working=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = '\100', c1 = '\200', c2 = '\201'; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + return 1; + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + return 1; + } + return 0; + } + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_memcmp_working=yes +else + ac_cv_func_memcmp_working=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_memcmp_working" >&5 +$as_echo "$ac_cv_func_memcmp_working" >&6; } +test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in + *" memcmp.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" + ;; +esac + + +for ac_func in atexit getcwd memmove memset strcasecmp strchr strdup strerror strncasecmp strrchr strstr strtol fchmod +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# 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" + + + + + + + + + + +### Configure Options + +# Add PCRE Studying + +# Check whether --enable-pcre-study was given. +if test "${enable_pcre_study+set}" = set; then : + enableval=$enable_pcre_study; + if test "$enableval" != "no"; then + pcre_study='-DWITH_PCRE_STUDY' + else + pcre_study='' + fi + +else + + pcre_study='-DWITH_PCRE_STUDY' + +fi + + +# Limit PCRE matching +# Check whether --enable-pcre-match-limit was given. +if test "${enable_pcre_match_limit+set}" = set; then : + enableval=$enable_pcre_match_limit; + if test "$enableval" = "yes"; then + as_fn_error "PCRE match limits require a numeric value" "$LINENO" 5 + elif test "$enableval" = "no"; then + pcre_match_limit='' + else + pcre_match_limit="-DMODSEC_PCRE_MATCH_LIMIT=$enableval" + fi + +else + + pcre_match_limit='-DMODSEC_PCRE_MATCH_LIMIT=1500' + +fi + + +# Limit PCRE matching recursion +# Check whether --enable-pcre-match-limit-recursion was given. +if test "${enable_pcre_match_limit_recursion+set}" = set; then : + enableval=$enable_pcre_match_limit_recursion; + if test "$enableval" = "yes"; then + as_fn_error "PCRE match limits require a numeric value" "$LINENO" 5 + elif test "$enableval" = "no"; then + pcre_match_limit_recursion='' + else + pcre_match_limit_recursion="-DMODSEC_PCRE_MATCH_LIMIT_RECURSION=$enableval" + fi + +else + + pcre_match_limit_recursion='-DMODSEC_PCRE_MATCH_LIMIT_RECURSION=1500' + +fi + + +# Ignore configure errors +# Check whether --enable-errors was given. +if test "${enable_errors+set}" = set; then : + enableval=$enable_errors; + if test "$enableval" != "no"; then + report_errors=1 + else + report_errors=0 + fi + +else + + report_errors=1 + +fi + + +# Verbose output +# Check whether --enable-verbose-output was given. +if test "${enable_verbose_output+set}" = set; then : + enableval=$enable_verbose_output; + if test "$enableval" != "no"; then + verbose_output=1 + else + verbose_output=0 + fi + +else + + verbose_output=0 + +fi + + +# Strict Compile +# Check whether --enable-strict-compile was given. +if test "${enable_strict_compile+set}" = set; then : + enableval=$enable_strict_compile; + 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 + +else + + strict_compile= + +fi + + +# DEBUG_CONF +# Check whether --enable-debug-conf was given. +if test "${enable_debug_conf+set}" = set; then : + enableval=$enable_debug_conf; + if test "$enableval" != "no"; then + debug_conf="-DDEBUG_CONF" + else + debug_conf= + fi + +else + + debug_conf= + +fi + + +# CACHE_DEBUG +# Check whether --enable-debug-cache was given. +if test "${enable_debug_cache+set}" = set; then : + enableval=$enable_debug_cache; + if test "$enableval" != "no"; then + debug_cache="-DCACHE_DEBUG" + else + debug_cache= + fi + +else + + debug_cache= + +fi + + +# DEBUG_ACMP +# Check whether --enable-debug-acmp was given. +if test "${enable_debug_acmp+set}" = set; then : + enableval=$enable_debug_acmp; + if test "$enableval" != "no"; then + debug_acmp="-DDEBUG_ACMP" + else + debug_acmp= + fi + +else + + debug_acmp= + +fi + + +# DEBUG_MEM +# Check whether --enable-debug-mem was given. +if test "${enable_debug_mem+set}" = set; then : + enableval=$enable_debug_mem; + if test "$enableval" != "no"; then + debug_mem="-DDEBUG_MEM" + else + debug_mem= + fi + +else + + debug_mem= + +fi + + +# PERFORMANCE_MEASUREMENT +# Check whether --enable-performance-measurement was given. +if test "${enable_performance_measurement+set}" = set; then : + enableval=$enable_performance_measurement; + if test "$enableval" != "no"; then + perf_meas="-DPERFORMANCE_MEASUREMENT" + else + perf_meas= + fi + +else + + perf_meas= + +fi + + +# NO_MODSEC_API +# Check whether --enable-modsec-api was given. +if test "${enable_modsec_api+set}" = set; then : + enableval=$enable_modsec_api; + if test "$enableval" != "yes"; then + modsec_api="-DNO_MODSEC_API" + else + modsec_api= + fi + +else + + modsec_api= + +fi + + +# Find apxs +{ $as_echo "$as_me:${as_lineno-$LINENO}: looking for Apache module support via DSO through APXS" >&5 +$as_echo "$as_me: looking for Apache module support via DSO through APXS" >&6;} + +# Check whether --with-apxs was given. +if test "${with_apxs+set}" = set; then : + withval=$with_apxs; + if test "$withval" = "yes"; then + APXS=apxs + else + APXS="$withval" + fi + +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 + { $as_echo "$as_me:${as_lineno-$LINENO}: found apxs at $APXS" >&5 +$as_echo "$as_me: found apxs at $APXS" >&6;} + { $as_echo "$as_me:${as_lineno-$LINENO}: checking httpd version" >&5 +$as_echo "$as_me: checking httpd version" >&6;} + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include "$APXS_INCLUDE/ap_mmn.h" +#if AP_MODULE_MAGIC_AT_LEAST($HTTPD_WANTED_MMN,0) +VERSION_OK +#endif +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "VERSION_OK" >/dev/null 2>&1; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: httpd is recent enough" >&5 +$as_echo "$as_me: httpd is recent enough" >&6;} +else + + if test "$report_errors" -eq 1; then + { as_fn_set_status mmn must be at least $HTTPD_WANTED_MMN +as_fn_error "apache is too old" "$LINENO" 5; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: apache is too old" >&5 +$as_echo "$as_me: apache is too old" >&mmn must be at least $HTTPD_WANTED_MMN;} + fi + +fi +rm -f conftest* + + fi + APXS_INCLUDEDIR="`$APXS -q INCLUDEDIR`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apxs INCLUDEDIR: $APXS_INCLUDEDIR" >&5 +$as_echo "$as_me: apxs INCLUDEDIR: $APXS_INCLUDEDIR" >&6;}; 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 { $as_echo "$as_me:${as_lineno-$LINENO}: apxs INCLUDES: $APXS_INCLUDES" >&5 +$as_echo "$as_me: apxs INCLUDES: $APXS_INCLUDES" >&6;}; fi + APXS_CFLAGS="`$APXS -q CFLAGS` `$APXS -q EXTRA_CFLAGS`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apxs CFLAGS: $APXS_CFLAGS" >&5 +$as_echo "$as_me: apxs CFLAGS: $APXS_CFLAGS" >&6;}; fi + APXS_LDFLAGS="`$APXS -q LDFLAGS` `$APXS -q EXTRA_LDFLAGS`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apxs LDFLAGS: $APXS_LDFLAGS" >&5 +$as_echo "$as_me: apxs LDFLAGS: $APXS_LDFLAGS" >&6;}; fi + APXS_LIBDIR="`$APXS -q LIBDIR`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apxs LIBDIR: $APXS_LIBDIR" >&5 +$as_echo "$as_me: apxs LIBDIR: $APXS_LIBDIR" >&6;}; 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 { $as_echo "$as_me:${as_lineno-$LINENO}: apxs LIBS: $APXS_LIBS" >&5 +$as_echo "$as_me: apxs LIBS: $APXS_LIBS" >&6;}; fi + APXS_LIBTOOL="`$APXS -q LIBTOOL`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apxs LIBTOOL: $APXS_LIBTOOL" >&5 +$as_echo "$as_me: apxs LIBTOOL: $APXS_LIBTOOL" >&6;}; fi + APXS_CC="`$APXS -q CC`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apxs CC: $APXS_CC" >&5 +$as_echo "$as_me: apxs CC: $APXS_CC" >&6;}; fi + APXS_BINDIR="`$APXS -q BINDIR`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apxs BINDIR: $APXS_BINDIR" >&5 +$as_echo "$as_me: apxs BINDIR: $APXS_BINDIR" >&6;}; fi + APXS_SBINDIR="`$APXS -q SBINDIR`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apxs SBINDIR: $APXS_SBINDIR" >&5 +$as_echo "$as_me: apxs SBINDIR: $APXS_SBINDIR" >&6;}; fi + APXS_PROGNAME="`$APXS -q PROGNAME`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apxs PROGNAME: $APXS_PROGNAME" >&5 +$as_echo "$as_me: apxs PROGNAME: $APXS_PROGNAME" >&6;}; fi + APXS_LIBEXECDIR="`$APXS -q LIBEXECDIR`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apxs LIBEXECDIR: $APXS_LIBEXECDIR" >&5 +$as_echo "$as_me: apxs LIBEXECDIR: $APXS_LIBEXECDIR" >&6;}; 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 { $as_echo "$as_me:${as_lineno-$LINENO}: apxs HTTPD: $APXS_HTTPD" >&5 +$as_echo "$as_me: apxs HTTPD: $APXS_HTTPD" >&6;}; fi +else + if test "$report_errors" -eq 1; then + as_fn_error "couldn't find APXS" "$LINENO" 5 + else + { $as_echo "$as_me:${as_lineno-$LINENO}: couldn't find APXS" >&5 +$as_echo "$as_me: couldn't find APXS" >&6;} + fi +fi + +# Include M4 macros + +PCRE_CONFIG="" +PCRE_CFLAGS="" +PCRE_LIBS="" + + + + +APR_CONFIG="" +APR_CFLAGS="" +APR_LDFLAGS="" +APR_LIBS="" +APR_LINK_LD="" + + + + +APU_CONFIG="" +APU_CFLAGS="" +APU_LDFLAGS="" +APU_LIBS="" +APU_LINK_LD="" + + + + +LIBXML2_CONFIG="" +LIBXML2_CFLAGS="" +LIBXML2_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" + + + + +CURL_CONFIG="" +CURL_CFLAGS="" +CURL_LIBS="" +CURL_MIN_VERSION="7.15.1" + + + + + +### 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" + + + + + + + + + + + + + + + + + + + + + + + +# Check whether --with-pcre was given. +if test "${with_pcre+set}" = set; then : + withval=$with_pcre; test_paths="${with_pcre}" +else + test_paths="/usr/local/libpcre /usr/local/pcre /usr/local /opt/libpcre /opt/pcre /opt /usr" +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libpcre config script" >&5 +$as_echo_n "checking for libpcre config script... " >&6; } + +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 + if test ! -d "$x" -a -e "$x"; then + PCRE_CONFIG=$x + pcre_path="no" + break + fi + + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${PCRE_CONFIG}" >&5 +$as_echo "${PCRE_CONFIG}" >&6; } + PCRE_CFLAGS="`${PCRE_CONFIG} --cflags`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: pcre CFLAGS: $PCRE_CFLAGS" >&5 +$as_echo "$as_me: pcre CFLAGS: $PCRE_CFLAGS" >&6;}; fi + PCRE_LIBS="`${PCRE_CONFIG} --libs`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: pcre LIBS: $PCRE_LIBS" >&5 +$as_echo "$as_me: pcre LIBS: $PCRE_LIBS" >&6;}; fi + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + +if test -z "${PCRE_LIBS}"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: *** pcre library not found." >&5 +$as_echo "$as_me: *** pcre library not found." >&6;} + as_fn_error "pcre library is required" "$LINENO" 5 +else + { $as_echo "$as_me:${as_lineno-$LINENO}: using '${PCRE_LIBS}' for pcre Library" >&5 +$as_echo "$as_me: using '${PCRE_LIBS}' for pcre Library" >&6;} + +fi + + + +# Check whether --with-apr was given. +if test "${with_apr+set}" = set; then : + withval=$with_apr; test_paths="${with_apr}" +else + test_paths="/usr/local/libapr /usr/local/apr /usr/local /opt/libapr /opt/apr /opt /usr" +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libapr config script" >&5 +$as_echo_n "checking for libapr config script... " >&6; } + +for x in ${test_paths}; do + if test ! -d "$x" -a -e "$x"; then + APR_CONFIG=$x + apr_path=no + break + fi + + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${APR_CONFIG}" >&5 +$as_echo "${APR_CONFIG}" >&6; } + APR_CFLAGS="`${APR_CONFIG} --includes --cppflags --cflags`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apr CFLAGS: $APR_CFLAGS" >&5 +$as_echo "$as_me: apr CFLAGS: $APR_CFLAGS" >&6;}; fi + APR_LDFLAGS="`${APR_CONFIG} --ldflags`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apr LDFLAGS: $APR_LDFLAGS" >&5 +$as_echo "$as_me: apr LDFLAGS: $APR_LDFLAGS" >&6;}; fi + APR_LIBS="`${APR_CONFIG} --libs`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apr LIBS: $APR_LIBS" >&5 +$as_echo "$as_me: apr LIBS: $APR_LIBS" >&6;}; fi + APR_LINK_LD="`${APR_CONFIG} --link-ld`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apr LINK_LD: $APR_LINK_LD" >&5 +$as_echo "$as_me: apr LINK_LD: $APR_LINK_LD" >&6;}; fi + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + + +if test -z "${APR_LIBS}"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: *** apr library not found." >&5 +$as_echo "$as_me: *** apr library not found." >&6;} + as_fn_error "apr library is required" "$LINENO" 5 +else + { $as_echo "$as_me:${as_lineno-$LINENO}: using '${APR_LIBS}' for apr Library" >&5 +$as_echo "$as_me: using '${APR_LIBS}' for apr Library" >&6;} + +fi + + + +# Check whether --with-apu was given. +if test "${with_apu+set}" = set; then : + withval=$with_apu; test_paths="${with_apu}" +else + 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" +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libapu config script" >&5 +$as_echo_n "checking for libapu config script... " >&6; } + +for x in ${test_paths}; do + if test ! -d "$x" -a -e "$x"; then + APU_CONFIG=$x + apu_path="no" + break + fi + + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${APU_CONFIG}" >&5 +$as_echo "${APU_CONFIG}" >&6; } + APU_CFLAGS="`${APU_CONFIG} --includes`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apu CFLAGS: $APU_CFLAGS" >&5 +$as_echo "$as_me: apu CFLAGS: $APU_CFLAGS" >&6;}; fi + APU_LDFLAGS="`${APU_CONFIG} --ldflags`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apu LDFLAGS: $APU_LDFLAGS" >&5 +$as_echo "$as_me: apu LDFLAGS: $APU_LDFLAGS" >&6;}; fi + APU_LIBS="`${APU_CONFIG} --libs`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apu LIBS: $APU_LIBS" >&5 +$as_echo "$as_me: apu LIBS: $APU_LIBS" >&6;}; fi + APU_LINK_LD="`${APU_CONFIG} --link-ld`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: apu LINK_LD: $APU_LINK_LD" >&5 +$as_echo "$as_me: apu LINK_LD: $APU_LINK_LD" >&6;}; fi + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + + +if test -z "${APU_LIBS}"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: *** apu library not found." >&5 +$as_echo "$as_me: *** apu library not found." >&6;} + as_fn_error "apu library is required" "$LINENO" 5 +else + { $as_echo "$as_me:${as_lineno-$LINENO}: using '${APU_LIBS}' for apu Library" >&5 +$as_echo "$as_me: using '${APU_LIBS}' for apu Library" >&6;} + +fi + + + +# Check whether --with-libxml was given. +if test "${with_libxml+set}" = set; then : + withval=$with_libxml; test_paths="${with_libxml}" +else + test_paths="/usr/local/libxml2 /usr/local/xml2 /usr/local/xml /usr/local /opt/libxml2 /opt/libxml /opt/xml2 /opt/xml /opt /usr" +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libxml2 config script" >&5 +$as_echo_n "checking for libxml2 config script... " >&6; } + +for x in ${test_paths}; do + if test ! -d "$x" -a -e "$x"; then + LIBXML2_CONFIG=$x + libxml2_path="no" + break + fi + + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${LIBXML2_CONFIG}" >&5 +$as_echo "${LIBXML2_CONFIG}" >&6; } + LIBXML2_CFLAGS="`${LIBXML2_CONFIG} --cflags`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: xml CFLAGS: $LIBXML2_CFLAGS" >&5 +$as_echo "$as_me: xml CFLAGS: $LIBXML2_CFLAGS" >&6;}; fi + LIBXML2_LIBS="`${LIBXML2_CONFIG} --libs`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: xml LIBS: $LIBXML2_LIBS" >&5 +$as_echo "$as_me: xml LIBS: $LIBXML2_LIBS" >&6;}; fi + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + +if test -z "${LIBXML2_LIBS}"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: *** xml library not found." >&5 +$as_echo "$as_me: *** xml library not found." >&6;} + as_fn_error "libxml2 is required" "$LINENO" 5 +else + { $as_echo "$as_me:${as_lineno-$LINENO}: using '${LIBXML2_LIBS}' for libxml2" >&5 +$as_echo "$as_me: using '${LIBXML2_LIBS}' for libxml2" >&6;} + +fi + + + +# Check whether --with-lua was given. +if test "${with_lua+set}" = set; then : + withval=$with_lua; test_paths="${with_lua}" +else + test_paths="/usr/local/liblua /usr/local/lua /usr/local /opt/liblua /opt/lua /opt /usr"; +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for liblua config script" >&5 +$as_echo_n "checking for liblua config script... " >&6; } + +for x in ${test_paths}; do + if test ! -d "$x" -a -e "$x"; then + LUA_CONFIG=$x + break + fi + + 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 + +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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${LUA_CONFIG}" >&5 +$as_echo "${LUA_CONFIG}" >&6; } + LUA_CFLAGS="`${LUA_CONFIG} ${LUA_PKGNAME} --cflags`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: lua CFLAGS: $LUA_CFLAGS" >&5 +$as_echo "$as_me: lua CFLAGS: $LUA_CFLAGS" >&6;}; fi + LUA_LIBS="`${LUA_CONFIG} ${LUA_PKGNAME} --libs`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: lua LIBS: $LUA_LIBS" >&5 +$as_echo "$as_me: lua LIBS: $LUA_LIBS" >&6;}; fi + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lua install" >&5 +$as_echo_n "checking for lua install... " >&6; } + 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 + + 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="" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lua_lib_path} ${lua_inc_path}" >&5 +$as_echo "${lua_lib_path} ${lua_inc_path}" >&6; } + LUA_CFLAGS="-I${lua_inc_path}" + LUA_LIBS="-L${lua_lib_path} -l${lua_lib_name}" + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi +fi + +if test -n "${LUA_LIBS}"; then + LUA_CFLAGS="-DWITH_LUA ${LUA_CFLAGS}" +fi + + + + +if test "${with_path}" != "no"; then + if test -z "${LUA_LIBS}"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: optional lua library not found" >&5 +$as_echo "$as_me: optional lua library not found" >&6;} + else + { $as_echo "$as_me:${as_lineno-$LINENO}: using '${LUA_LIBS}' for lua Library" >&5 +$as_echo "$as_me: using '${LUA_LIBS}' for lua Library" >&6;} + + fi +fi + + + +# Check whether --with-curl was given. +if test "${with_curl+set}" = set; then : + withval=$with_curl; test_paths="${with_curl}" +else + test_paths="/usr/local/libcurl /usr/local/curl /usr/local /opt/libcurl /opt/curl /opt /usr" +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcurl config script" >&5 +$as_echo_n "checking for libcurl config script... " >&6; } + +for x in ${test_paths}; do + if test ! -d "$x" -a -e "$x"; then + CURL_CONFIG=$x + curl_path="no" + break + fi + + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${CURL_CONFIG}" >&5 +$as_echo "${CURL_CONFIG}" >&6; } + CURL_CFLAGS="`${CURL_CONFIG} --cflags`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: curl CFLAGS: $CURL_CFLAGS" >&5 +$as_echo "$as_me: curl CFLAGS: $CURL_CFLAGS" >&6;}; fi + CURL_LIBS="`${CURL_CONFIG} --libs`" + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: curl LIBS: $CURL_LIBS" >&5 +$as_echo "$as_me: curl LIBS: $CURL_LIBS" >&6;}; fi + CURL_VERSION=`${CURL_CONFIG} --version | sed 's/^[^0-9][^[:space:]][^[:space:]]*[[:space:]]*//'` + if test "$verbose_output" -eq 1; then { $as_echo "$as_me:${as_lineno-$LINENO}: curl VERSION: $CURL_VERSION" >&5 +$as_echo "$as_me: curl VERSION: $CURL_VERSION" >&6;}; fi + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libcurl is at least v${CURL_MIN_VERSION}" >&5 +$as_echo_n "checking if libcurl is at least v${CURL_MIN_VERSION}... " >&6; } + 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: NOTE: curl library may be too old: $CURL_VERSION" >&5 +$as_echo "$as_me: NOTE: curl library may be too old: $CURL_VERSION" >&6;} + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libcurl is linked with gnutls" >&5 +$as_echo_n "checking if libcurl is linked with gnutls... " >&6; } + curl_uses_gnutls=`echo ${CURL_LIBS} | grep gnutls | wc -l` + if test "$curl_uses_gnutls" -ne 0; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: NOTE: curl linked with gnutls may be buggy, openssl recommended" >&5 +$as_echo "$as_me: NOTE: curl linked with gnutls may be buggy, openssl recommended" >&6;} + CURL_USES_GNUTLS=yes + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CURL_USES_GNUTLS=no + fi + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + +if test -z "${CURL_LIBS}"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: *** curl library not found." >&5 +$as_echo "$as_me: *** curl library not found." >&6;} + { $as_echo "$as_me:${as_lineno-$LINENO}: NOTE: curl library is only required for building mlogc" >&5 +$as_echo "$as_me: NOTE: curl library is only required for building mlogc" >&6;} +else + { $as_echo "$as_me:${as_lineno-$LINENO}: using '${CURL_LIBS}' for curl Library" >&5 +$as_echo "$as_me: using '${CURL_LIBS}' for curl Library" >&6;} + +fi + + +ac_config_files="$ac_config_files Makefile" + +ac_config_files="$ac_config_files build/apxs-wrapper" + +if test -e "$PERL"; then + ac_config_files="$ac_config_files mlogc-src/mlogc-batch-load.pl" + + ac_config_files="$ac_config_files t/run-unit-tests.pl" + + ac_config_files="$ac_config_files t/run-regression-tests.pl" + + ac_config_files="$ac_config_files t/gen_rx-pm.pl" + + ac_config_files="$ac_config_files t/csv_rx-pm.pl" + + ac_config_files="$ac_config_files t/regression/server_root/conf/httpd.conf" + + + # Perl based tools + ac_config_files="$ac_config_files ../tools/rules-updater.pl" + +fi +if test -e "mlogc-src/Makefile.in"; then + ac_config_files="$ac_config_files mlogc-src/Makefile" + +fi + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error ERROR [LINENO LOG_FD] +# --------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with status $?, using 1 if that was 0. +as_fn_error () +{ + as_status=$?; test $as_status -eq 0 && as_status=1 + if test "$3"; then + as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 + fi + $as_echo "$as_me: error: $1" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.64. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.64, + with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2009 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "mod_security2_config.h") CONFIG_HEADERS="$CONFIG_HEADERS mod_security2_config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "build/apxs-wrapper") CONFIG_FILES="$CONFIG_FILES build/apxs-wrapper" ;; + "mlogc-src/mlogc-batch-load.pl") CONFIG_FILES="$CONFIG_FILES mlogc-src/mlogc-batch-load.pl" ;; + "t/run-unit-tests.pl") CONFIG_FILES="$CONFIG_FILES t/run-unit-tests.pl" ;; + "t/run-regression-tests.pl") CONFIG_FILES="$CONFIG_FILES t/run-regression-tests.pl" ;; + "t/gen_rx-pm.pl") CONFIG_FILES="$CONFIG_FILES t/gen_rx-pm.pl" ;; + "t/csv_rx-pm.pl") CONFIG_FILES="$CONFIG_FILES t/csv_rx-pm.pl" ;; + "t/regression/server_root/conf/httpd.conf") CONFIG_FILES="$CONFIG_FILES t/regression/server_root/conf/httpd.conf" ;; + "../tools/rules-updater.pl") CONFIG_FILES="$CONFIG_FILES ../tools/rules-updater.pl" ;; + "mlogc-src/Makefile") CONFIG_FILES="$CONFIG_FILES mlogc-src/Makefile" ;; + + *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5 + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\).*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\).*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ + || as_fn_error "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_t=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_t"; then + break + elif $ac_last_try; then + as_fn_error "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin" \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out" && rm -f "$tmp/out";; + *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; + esac \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" + } >"$tmp/config.h" \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$tmp/config.h" "$ac_file" \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error "could not create -" "$LINENO" 5 + fi + ;; + + + esac + + + case $ac_file$ac_mode in + "build/apxs-wrapper":F) chmod +x build/apxs-wrapper ;; + "mlogc-src/mlogc-batch-load.pl":F) chmod +x mlogc-src/mlogc-batch-load.pl ;; + "t/run-unit-tests.pl":F) chmod +x t/run-unit-tests.pl ;; + "t/run-regression-tests.pl":F) chmod +x t/run-regression-tests.pl ;; + "t/gen_rx-pm.pl":F) chmod +x t/gen_rx-pm.pl ;; + "t/csv_rx-pm.pl":F) chmod +x t/csv_rx-pm.pl ;; + "../tools/rules-updater.pl":F) chmod +x ../tools/rules-updater.pl ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit $? +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/2.5.13/2.5.x/apache2/configure.in b/2.5.13/2.5.x/apache2/configure.in new file mode 100644 index 00000000..1eb935c5 --- /dev/null +++ b/2.5.13/2.5.x/apache2/configure.in @@ -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 diff --git a/2.5.13/2.5.x/apache2/mlogc-src/INSTALL b/2.5.13/2.5.x/apache2/mlogc-src/INSTALL new file mode 100644 index 00000000..095f5a4d --- /dev/null +++ b/2.5.13/2.5.x/apache2/mlogc-src/INSTALL @@ -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. + diff --git a/2.5.13/2.5.x/apache2/mlogc-src/Makefile.in b/2.5.13/2.5.x/apache2/mlogc-src/Makefile.in new file mode 100755 index 00000000..6b73c4d8 --- /dev/null +++ b/2.5.13/2.5.x/apache2/mlogc-src/Makefile.in @@ -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 + diff --git a/2.5.13/2.5.x/apache2/mlogc-src/Makefile.win b/2.5.13/2.5.x/apache2/mlogc-src/Makefile.win new file mode 100755 index 00000000..a720bcb3 --- /dev/null +++ b/2.5.13/2.5.x/apache2/mlogc-src/Makefile.win @@ -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 diff --git a/2.5.13/2.5.x/apache2/mlogc-src/mlogc-batch-load.pl.in b/2.5.13/2.5.x/apache2/mlogc-src/mlogc-batch-load.pl.in new file mode 100755 index 00000000..f7bd240a --- /dev/null +++ b/2.5.13/2.5.x/apache2/mlogc-src/mlogc-batch-load.pl.in @@ -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 \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 = ) { + $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}, + ); + +} + diff --git a/2.5.13/2.5.x/apache2/mlogc-src/mlogc-default.conf b/2.5.13/2.5.x/apache2/mlogc-src/mlogc-default.conf new file mode 100644 index 00000000..919b1adc --- /dev/null +++ b/2.5.13/2.5.x/apache2/mlogc-src/mlogc-default.conf @@ -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 + diff --git a/2.5.13/2.5.x/apache2/mlogc-src/mlogc.c b/2.5.13/2.5.x/apache2/mlogc-src/mlogc.c new file mode 100644 index 00000000..36717f40 --- /dev/null +++ b/2.5.13/2.5.x/apache2/mlogc-src/mlogc.c @@ -0,0 +1,2311 @@ +/* + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if APR_HAVE_UNISTD_H +#include /* for getpid() */ +#endif +#include +#include +#include +#include +#include + +#include "msc_release.h" + +static void logc_shutdown(int rc); +static void create_new_worker(int lock); +static void error_log(int level, void *thread, + const char *text, ...) PRINTF_ATTRIBUTE(3,4); + + +/* -- Constants -- */ + +/* Error log levels. */ +#define LOG_ERROR 1 +#define LOG_WARNING 2 +#define LOG_NOTICE 3 +#define LOG_DEBUG 4 +#define LOG_DEBUG2 5 + +/* The management thread will wake up every five seconds. */ +#define MANAGER_SLEEP 5000000 +#define MANAGER_SUBSLEEP 10000 + +/* Hack to allow multiple mlogc with single delete */ +#define KEEP_ENTRIES_REMOVE_HACK 2600 +#define KEEP_ENTRIES_REMOVE_TIME 0l +#ifdef TEST_HACK +#define TEST_WITH_RAND_SLEEP(n) \ +do { \ + int sec = rand()/(RAND_MAX/n); \ + error_log(LOG_DEBUG2, NULL, "TEST_HACK: Sleeping for %ds", sec); \ + apr_sleep(apr_time_from_sec(sec)); \ +} while(0) +#else +#define TEST_WITH_RAND_SLEEP(n) +#endif + +#define CAPTUREVECTORSIZE 60 +#define PIPE_BUF_SIZE 65536 +#define MEMALLOC_ERROR_MSG "Memory allocation failed!" +#define VERSION MODSEC_VERSION + +#define CMDLINE_OPTS "fvh" + +#define TXIN 0 +#define TXOUT 1 + +#define STATUSBUF_SIZE 256 + +#define ISHEXCHAR(X) ( ((X >= '0')&&(X <= '9')) \ + || ((X >= 'a')&&(X <= 'f')) \ + || ((X >= 'A')&&(X <= 'F')) ) + +/* -- Regex Patterns -- */ + +/** + * This regular expression is used to parse the entire + * log line we receive from Apache. The REQUEST_LINE is + * treated as a single parameter to allow for invalid + * requests. + */ +static const char logline_pattern[] = + "^(\\S+)" + "\\ (\\S+)\\ (\\S+)\\ (\\S+)" + "\\ \\[([^:]+):(\\d+:\\d+:\\d+)\\ ([^\\]]+)\\]" + "\\ \"(.*)\"" + "\\ (\\d+)\\ (\\S+)" + "\\ \"(.*)\"\\ \"(.*)\"" + "\\ (\\S+)\\ \"(.*)\"" + "\\ /?(\\S+)\\ (\\d+)\\ (\\d+)" + "\\ (\\S+)" + "(.*)$"; + + +/** + * This regular expression can be used to parse + * a REQUEST_LINE field into method, URI, and + * protocol. + */ +static const char requestline_pattern[] = + "(\\S+)\\ (.*?)\\ (\\S+)"; + + +/* -- Structures -- */ + +typedef struct { + unsigned long int id; + const char *line; + apr_size_t line_size; +} entry_t; + + +/* -- Global variables -- */ + +static pid_t logc_pid = 0; +static const char *conffile = NULL; +static const char *lockfile = NULL; +static int have_read_data = 0; +static int checkpoint_interval = 60; +static apr_time_t checkpoint_time_last = 0; +static const char *collector_root = NULL; +static apr_table_t *conf = NULL; +static const char *console_uri = NULL; +static apr_array_header_t *curl_handles = NULL; +static int current_workers = 0; +static int management_thread_active = 0; +static unsigned long int entry_counter = 1; +static const char *error_log_path = NULL; +static apr_file_t *error_log_fd = NULL; +static int error_log_level = 2; +static apr_hash_t *in_progress = NULL; +static int keep_alive = 150; /* Not used yet. */ +static int keep_alive_timeout = 300; /* Not used yet. */ +static int keep_entries = 0; +static const char *log_repository = NULL; +static void *logline_regex = NULL; +static int max_connections = 10; +static int max_worker_requests = 1000; +static apr_global_mutex_t *gmutex = NULL; +static apr_thread_mutex_t *mutex = NULL; +static apr_pool_t *pool = NULL; +static apr_pool_t *thread_pool = NULL; +static apr_pool_t *recv_pool = NULL; +static apr_array_header_t *queue = NULL; +static const char *queue_path = NULL; +/* static apr_time_t queue_time = 0; */ +static void *requestline_regex = NULL; +static int running = 0; +static const char *sensor_password = NULL; +static const char *sensor_username = NULL; +static int server_error = 0; +static apr_time_t server_error_last_check_time = 0; +static int server_error_timeout = 60; +static int startup_delay = 100; +static int transaction_delay = 100; +static const char *transaction_log_path = NULL; +static apr_file_t *transaction_log_fd = NULL; + + +/* -- Commandline opts -- */ +static int opt_force = 0; + +/* -- Code -- */ + +static char *_log_escape(apr_pool_t *mp, const char *input, + apr_size_t input_len) +{ + static const char c2x_table[] = "0123456789abcdef"; + unsigned char *d = NULL; + char *ret = NULL; + unsigned long int i; + + if (input == NULL) return NULL; + + ret = apr_palloc(mp, input_len * 4 + 1); + if (ret == NULL) return NULL; + d = (unsigned char *)ret; + + i = 0; + while(i < input_len) { + switch(input[i]) { + case '"' : + *d++ = '\\'; + *d++ = '"'; + break; + case '\b' : + *d++ = '\\'; + *d++ = 'b'; + break; + case '\n' : + *d++ = '\\'; + *d++ = 'n'; + break; + case '\r' : + *d++ = '\\'; + *d++ = 'r'; + break; + case '\t' : + *d++ = '\\'; + *d++ = 't'; + break; + case '\v' : + *d++ = '\\'; + *d++ = 'v'; + break; + case '\\' : + *d++ = '\\'; + *d++ = '\\'; + break; + default : + if ((input[i] <= 0x1f)||(input[i] >= 0x7f)) { + *d++ = '\\'; + *d++ = 'x'; + *d++ = c2x_table[input[i] >> 4]; + *d++ = c2x_table[input[i] & 0x0f]; + } else { + *d++ = input[i]; + } + break; + } + + i++; + } + + *d = 0; + + return ret; +} + +/** + * Converts a byte given as its hexadecimal representation + * into a proper byte. Handles uppercase and lowercase letters + * but does not check for overflows. + */ +static unsigned char x2c(unsigned char *what) { + register unsigned char digit; + + digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); + digit *= 16; + digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0')); + + return digit; +} + +/** + * URL Decodes a string in-place + */ +static int urldecode_inplace(unsigned char *input, apr_size_t input_len) { + unsigned char *d = (unsigned char *)input; + apr_size_t i; + + if (input == NULL) return 0; + + i = 0; + while (i < input_len) { + if (input[i] == '%') { + /* Character is a percent sign. */ + + /* Are there enough bytes available? */ + if (i + 2 < input_len) { + char c1 = input[i + 1]; + char c2 = input[i + 2]; + + if (ISHEXCHAR(c1) && ISHEXCHAR(c2)) { + /* Valid encoding - decode it. */ + *d++ = x2c(&input[i + 1]); + i += 3; + } else { + /* Not a valid encoding, skip this % */ + *d++ = input[i++]; + } + } else { + /* Not enough bytes available, copy the raw bytes. */ + *d++ = input[i++]; + } + } else { + /* Character is not a percent sign. */ + if (input[i] == '+') { + *d++ = ' '; + } else { + *d++ = input[i]; + } + i++; + } + } + + *d = '\0'; + + return 1; +} + +/** + * Detect a relative path and merge it with the collector root + * path. Leave absolute paths as they are. + */ +static const char *file_path(const char *path) +{ + char *newpath = NULL; + apr_status_t rc; + + if (path == NULL) return NULL; + + rc = apr_filepath_merge(&newpath, collector_root, + path, APR_FILEPATH_TRUENAME, pool); + if ((newpath != NULL) && (rc == APR_SUCCESS || APR_STATUS_IS_EPATHWILD(rc) + || APR_STATUS_IS_ENOENT(rc) || APR_STATUS_IS_ENOTDIR(rc))) + { + return newpath; + } + else { + return NULL; + } +} + + +/** + * Returns the current datetime as a string. + */ +static char *current_logtime(char *dest, int dlen) +{ + apr_time_exp_t t; + apr_size_t len; + + apr_time_exp_lt(&t, apr_time_now()); + apr_strftime(dest, &len, dlen, "%a %b %d %H:%M:%S %Y", &t); + + return dest; +} + + +/** + * Logs error to the error log (if available) or + * to the stderr. + */ +static void error_log(int level, void *thread, const char *text, ...) +{ + char msg1[4096] = ""; + char msg2[4096] = ""; + char datetime[100]; + va_list ap; + + if (level > error_log_level) return; + + va_start(ap, text); + + apr_vsnprintf(msg1, sizeof(msg1), text, ap); + apr_snprintf(msg2, sizeof(msg2), "[%s] [%d] [%" APR_PID_T_FMT "/%pp] %s\n", + current_logtime(datetime, sizeof(datetime)), + level, logc_pid, (thread ? thread : 0), msg1); + + if (error_log_fd != NULL) { + apr_size_t nbytes_written; + apr_size_t nbytes = strlen(msg2); + apr_file_write_full(error_log_fd, msg2, nbytes, &nbytes_written); + } + else { + fprintf(stderr, "%s", msg2); + } + + va_end(ap); +} + + +/** + * Adds one entry to the internal queue. It will (optionally) start + * a new thread to handle it. + */ +static void add_entry(const char *data, int start_worker) +{ + entry_t *entry = NULL; + + entry = (entry_t *)malloc(sizeof(entry_t)); + entry->id = 0; + entry->line = strdup(data); + entry->line_size = strlen(entry->line); + + error_log(LOG_DEBUG, NULL, "Queue locking thread mutex."); + if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mutex))) { + error_log(LOG_DEBUG, NULL, "Queue waiting on thread mutex."); + apr_thread_mutex_lock(mutex); + } + + /* Assign unique ID to this log entry. */ + entry->id = entry_counter++; + + /* Add the new audit log entry to the queue. */ + *(entry_t **)apr_array_push(queue) = entry; + + /* Create a new worker if we can, but not if there is a + * known problem with the server. + */ + if ( (start_worker != 0) + && (current_workers < max_connections)&&(server_error == 0)) + { + create_new_worker(0); + } + + error_log(LOG_DEBUG, NULL, "Queue unlocking thread mutex."); + apr_thread_mutex_unlock(mutex); +} + + +/** + * Read the queue entries. + */ +static int read_queue_entries(apr_file_t *fd, apr_time_t *queue_time) +{ + char linebuf[4100]; + int line_count = -1; + + for(;;) { + apr_status_t rc = apr_file_gets(linebuf, 4096, fd); + char *p; + + if (rc == APR_EOF) break; + if (rc != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, "Error reading from the queue file."); + logc_shutdown(1); + } + + if (line_count < 0) { + /* First line contains the queue time. */ + *queue_time = (apr_time_t)apr_atoi64(linebuf); + line_count = 0; + continue; + } + + p = &linebuf[0]; + + /* Remove the \n from the end of the line. */ + while(*p != '\0') { + if (*p == '\n') { + *p = '\0'; + break; + } + p++; + } + + if (linebuf[0] == '#') { /* Ignore comments. */ + continue; + } + + add_entry((const char *)&linebuf, 0); + + line_count++; + } + + apr_file_close(fd); + + return line_count; +} + + +/** + * Initialise the transaction log. This code should be + * executed only once at startup. + */ +static void transaction_log_init(void) +{ + /* ENH: These big enough? */ + char new_queue_path[256]; + char old_queue_path[256]; + apr_file_t *queue_fd = NULL; + apr_time_t queue_time; + + apr_snprintf(new_queue_path, sizeof(new_queue_path), "%s.new", queue_path); + apr_snprintf(old_queue_path, sizeof(old_queue_path), "%s.old", queue_path); + + /* Put a lock in place to ensure exclusivity. */ + error_log(LOG_DEBUG, NULL, + "Transaction initialization locking global mutex."); + if (APR_STATUS_IS_EBUSY(apr_global_mutex_trylock(gmutex))) { + error_log(LOG_DEBUG, NULL, + "Transaction initialization waiting on global mutex."); + apr_global_mutex_lock(gmutex); + } + + error_log(LOG_DEBUG, NULL, "Transaction initialization started."); + + /* Delete .new file if there is one. */ + apr_file_remove(new_queue_path, pool); + + /* Read in the data from the queue. */ + if (apr_file_open(&queue_fd, queue_path, APR_READ | APR_FILE_NOCLEANUP, + 0, pool) == APR_SUCCESS) + { + int line_count = read_queue_entries(queue_fd, &queue_time); + + apr_file_close(queue_fd); + + if (line_count > 0) { + error_log(LOG_NOTICE, NULL, + "Loaded %d entries from the queue file.", line_count); + } + } + /* Try the old queue file. */ + else if (apr_file_open(&queue_fd, old_queue_path, + APR_READ | APR_FILE_NOCLEANUP, + 0, pool) == APR_SUCCESS) + { + int line_count = read_queue_entries(queue_fd, &queue_time); + apr_file_close(queue_fd); + error_log(LOG_NOTICE, NULL, + "Loaded %d entries from the OLD queue file.", line_count); + apr_file_rename(old_queue_path, queue_path, pool); + } + else { + error_log(LOG_NOTICE, NULL, + "Queue file not found. New one will be created."); + } + + /* Delete the old queue file. */ + apr_file_remove(old_queue_path, pool); + + checkpoint_time_last = apr_time_now(); + + /* Start fresh with the transaction log. Do note that + * we do not truncate the transaction log on purpose. Apache + * will start copies of piped logging binaries during configuration + * testing. Truncating would erase the log of a currently running + * instance. + */ + if (apr_file_open(&transaction_log_fd, transaction_log_path, + APR_WRITE | APR_CREATE | APR_APPEND | APR_XTHREAD, + APR_OS_DEFAULT, pool) != APR_SUCCESS) + { + error_log(LOG_ERROR, NULL, + "Failed to open the transaction log: %s\n", + transaction_log_path); + error_log(LOG_DEBUG, NULL, + "Transaction initialization unlocking global mutex."); + apr_global_mutex_unlock(gmutex); + logc_shutdown(1); + } + + error_log(LOG_DEBUG, NULL, "Transaction initialization completed."); + + /* Unlock */ + error_log(LOG_DEBUG, NULL, + "Transaction initialization unlocking global mutex."); + apr_global_mutex_unlock(gmutex); +} + + +/** + * Log entry event (incoming or outgoing) to the transaction log. + */ +static void transaction_log(int direction, const char *entry) +{ + apr_size_t nbytes, nbytes_written; + char msg[8196] = ""; + + apr_snprintf(msg, sizeof(msg), "%u %s: %s\n", + (unsigned int)apr_time_sec(apr_time_now()), + (direction == TXIN ? "IN" : "OUT"), entry); + nbytes = strlen(msg); + apr_file_write_full(transaction_log_fd, msg, nbytes, &nbytes_written); +} + + +/** + * Executes a checkpoint, which causes the current queue to be + * written to a file and the transaction log to be truncated. + */ +static void transaction_checkpoint(void) +{ + /* ENH: These big enough? */ + char new_queue_path[256]; + char old_queue_path[256]; + apr_file_t *queue_fd = NULL; + apr_hash_index_t *hi = NULL; + char msg[256]; + int i; + apr_pool_t *cpool; + + apr_snprintf(new_queue_path, sizeof(new_queue_path), "%s.new", queue_path); + apr_snprintf(old_queue_path, sizeof(old_queue_path), "%s.old", queue_path); + apr_snprintf(msg, sizeof(msg), "%u\n", + (unsigned int)apr_time_sec(apr_time_now())); + + if (! have_read_data) { + error_log(LOG_DEBUG, NULL, "Checkpoint not required."); + return; + } + + /* Put a lock in place to ensure exclusivity. */ + error_log(LOG_DEBUG, NULL, "Checkpoint locking global mutex."); + if (APR_STATUS_IS_EBUSY(apr_global_mutex_trylock(gmutex))) { + error_log(LOG_DEBUG, NULL, "Checkpoint waiting on global mutex."); + apr_global_mutex_lock(gmutex); + } + + error_log(LOG_DEBUG, NULL, "Checkpoint started."); + + apr_pool_create(&cpool, NULL); + + /* Dump active entries into a new queue file. */ + if (apr_file_open(&queue_fd, new_queue_path, + APR_WRITE | APR_CREATE | APR_EXCL | + APR_TRUNCATE | APR_FILE_NOCLEANUP, + APR_OS_DEFAULT, cpool) != APR_SUCCESS) + { + error_log(LOG_ERROR, NULL, "Failed to create file: %s", new_queue_path); + error_log(LOG_DEBUG, NULL, "Checkpoint unlocking global mutex."); + apr_pool_destroy(cpool); + apr_global_mutex_unlock(gmutex); + return; + } + + /* Write the time first. */ + apr_file_write_full(queue_fd, msg, strlen(msg), NULL); + + /* Dump the entries sitting in the queue first. */ + for (i = 0; i < queue->nelts; i++) { + entry_t *entry = ((entry_t **)queue->elts)[i]; + apr_file_write_full(queue_fd, entry->line, entry->line_size, NULL); + apr_file_write_full(queue_fd, &"\n", 1, NULL); + } + error_log(LOG_DEBUG2, NULL, + "Checkpoint wrote %d queued entries to new queue.", i); + + /* Then dump the ones that are currently being processed. */ + i = 0; + for (hi = apr_hash_first(NULL, in_progress); + hi != NULL; hi = apr_hash_next(hi))\ + { + void *e; + entry_t *entry = NULL; + + i++; + apr_hash_this(hi, NULL, NULL, &e); + entry = e; /* quiet type-punned warning */ + apr_file_write_full(queue_fd, entry->line, entry->line_size, NULL); + apr_file_write_full(queue_fd, &"\n", 1, NULL); + } + error_log(LOG_DEBUG2, NULL, + "Checkpoint wrote %d additional entries to new queue.", i); + + apr_file_close(queue_fd); + + /* Switch the files and truncate the transaction log file. */ + apr_file_remove(old_queue_path, cpool); + apr_file_rename(queue_path, old_queue_path, cpool); + apr_file_rename(new_queue_path, queue_path, cpool); + apr_file_remove(old_queue_path, cpool); + apr_file_trunc(transaction_log_fd, 0); + + error_log(LOG_DEBUG, NULL, "Checkpoint completed."); + + apr_pool_destroy(cpool); + + /* Unlock and exit. */ + error_log(LOG_DEBUG, NULL, "Checkpoint unlocking global mutex."); + apr_global_mutex_unlock(gmutex); +} + + +/** + * Parse one confguration line and add it to the + * configuration table. + */ +static void parse_configuration_line(const char *line, int line_count) +{ + char *start = NULL, *command = NULL; + char *p = NULL; + + /* Remove the trailing newline character. */ + p = (char *)line; + while(*p != '\0') p++; + if ((p > start)&&(*(p - 1) == '\n')) *(p - 1) = '\0'; + + p = (char *)line; + /* Ignore whitespace at the beginning of the line. */ + while(apr_isspace(*p)) p++; + + /* Ignore empty lines and comments. */ + if ((*p == '\0')||(*p == '#')) return; + + start = p; + while(!apr_isspace(*p)&&(*p != '\0')) p++; + + command = apr_pstrmemdup(pool, start, p - start); + + while(apr_isspace(*p)) p++; + + /* Remove whitespace at the end. */ + start = p; + while(*p != '\0') p++; + if (p > start) { + p--; + while(apr_isspace(*p)) { + *p-- = '\0'; + } + } + + /* Remove quotes, but only if we have matching */ + if ((*start == '"') && (p > start) && (*p == '"')) { + start++; + *p-- = '\0'; + } + + /* Take the last directive */ + /* ENH: Error on dup directives? */ + apr_table_set(conf, command, start); +} + + +/** + * Reads configuration from a file. + */ +static void read_configuration(void) +{ + char linebuf[4096]; + apr_status_t rc; + apr_file_t *fd; + int line_count; + + conf = apr_table_make(pool, 32); + if (conf == NULL) { + error_log(LOG_ERROR, NULL, MEMALLOC_ERROR_MSG); + logc_shutdown(1); + } + + rc = apr_file_open(&fd, conffile, APR_READ | APR_FILE_NOCLEANUP, 0, pool); + if (rc != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, + "Unable to open configuration file: %s", conffile); + logc_shutdown(1); + } + + line_count = 0; + for(;;) { + rc = apr_file_gets(linebuf, 4096, fd); + if (rc == APR_EOF) return; + if (rc != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, + "Error reading from the configuration file."); + logc_shutdown(1); + } + + line_count++; + parse_configuration_line(linebuf, line_count); + } + + apr_file_close(fd); +} + + +/** + * Initialize the configuration. + */ +static void init_configuration(void) +{ + char errstr[1024]; + apr_status_t rc = 0; + const char *s = NULL; + + /* Other values may be based off the collector root. */ + s = apr_table_get(conf, "CollectorRoot"); + if (s != NULL) { + collector_root = s; + } + + /* Error Log */ + s = apr_table_get(conf, "ErrorLog"); + if (s != NULL) { + error_log_path = file_path(s); + } + + s = apr_table_get(conf, "ErrorLogLevel"); + if (s != NULL) { + error_log_level = atoi(s); + } + + if ((rc = apr_file_open(&error_log_fd, error_log_path, + APR_WRITE | APR_CREATE | APR_APPEND, + APR_OS_DEFAULT, pool)) != APR_SUCCESS) + { + error_log(LOG_ERROR, NULL, "Failed to open the error log %s: %s\n", + error_log_path, apr_strerror(rc, errstr, 1024)); + logc_shutdown(1); + } + + error_log(LOG_NOTICE, NULL, + "Configuring ModSecurity Audit Log Collector %s.", VERSION); + + /* Startup Delay */ + s = apr_table_get(conf, "StartupDelay"); + if (s != NULL) { + startup_delay = atoi(s); + } + + if ( startup_delay > 0 ) { + error_log(LOG_NOTICE, NULL, + "Delaying execution for %dms.", startup_delay); + apr_sleep(startup_delay * 1000); + error_log(LOG_DEBUG, NULL, + "Continuing execution after %dms delay.", startup_delay); + } + + /* Remaining Configuration */ + + error_log(LOG_DEBUG2, NULL, "CollectorRoot=%s", collector_root); + error_log(LOG_DEBUG2, NULL, "ErrorLog=%s", error_log_path); + error_log(LOG_DEBUG2, NULL, "ErrorLogLevel=%d", error_log_level); + error_log(LOG_DEBUG2, NULL, "StartupDelay=%d", startup_delay); + + s = apr_table_get(conf, "CheckpointInterval"); + if (s != NULL) { + checkpoint_interval = atoi(s); + error_log(LOG_DEBUG2, NULL, + "CheckpointInterval=%d", checkpoint_interval); + } + + s = apr_table_get(conf, "QueuePath"); + if (s != NULL) { + queue_path = file_path(s); + error_log(LOG_DEBUG2, NULL, "QueuePath=%s", queue_path); + } + else { + error_log(LOG_ERROR, NULL, + "QueuePath not defined in the configuration file."); + logc_shutdown(1); + } + + s = apr_table_get(conf, "LockFile"); + if (s != NULL) { + lockfile = file_path(s); + error_log(LOG_DEBUG2, NULL, "LockFile=%s", lockfile); + } + + s = apr_table_get(conf, "ServerErrorTimeout"); + if (s != NULL) { + server_error_timeout = atoi(s); + error_log(LOG_DEBUG2, NULL, + "ServerErrorTimeout=%d", server_error_timeout); + } + + s = apr_table_get(conf, "TransactionDelay"); + if (s != NULL) { + transaction_delay = atoi(s); + error_log(LOG_DEBUG2, NULL, "TransactionDelay=%d", transaction_delay); + } + + s = apr_table_get(conf, "TransactionLog"); + if (s != NULL) { + transaction_log_path = file_path(s); + error_log(LOG_DEBUG2, NULL, "TransactionLog=%s", transaction_log_path); + } + + s = apr_table_get(conf, "MaxConnections"); + if (s != NULL) { + int v = atoi(s); + if (v >= 0) max_connections = v; + error_log(LOG_DEBUG2, NULL, "MaxConnections=%d", max_connections); + } + + s = apr_table_get(conf, "MaxWorkerRequests"); + if (s != NULL) { + int v = atoi(s); + if (v >= 0) max_worker_requests = v; + error_log(LOG_DEBUG2, NULL, + "MaxWorkerRequests=%d", max_worker_requests); + } + + s = apr_table_get(conf, "KeepAlive"); + if (s != NULL) { + int v = atoi(s); + if (v >= 0) keep_alive = v; + error_log(LOG_DEBUG2, NULL, "KeepAlive=%d", keep_alive); + } + + s = apr_table_get(conf, "KeepAliveTimeout"); + if (s != NULL) { + int v = atoi(s); + if (v >= 0) keep_alive_timeout = v; + error_log(LOG_DEBUG2, NULL, "KeepAliveTimeout=%d", keep_alive_timeout); + } + + s = apr_table_get(conf, "LogStorageDir"); + if (s != NULL) { + log_repository = file_path(s); + error_log(LOG_DEBUG2, NULL, "LogStorageDir=%s", log_repository); + } + else { + error_log(LOG_ERROR, NULL, + "Missing mandatory parameter LogStorageDir.\n"); + logc_shutdown(1); + } + + s = apr_table_get(conf, "ConsoleURI"); + if (s != NULL) { + console_uri = s; + error_log(LOG_DEBUG2, NULL, "ConsoleURI=%s", console_uri); + } + else { + error_log(LOG_ERROR, NULL, "Missing mandatory parameter ConsoleURI.\n"); + logc_shutdown(1); + } + + s = apr_table_get(conf, "SensorUsername"); + if (s != NULL) { + sensor_username = s; + error_log(LOG_DEBUG2, NULL, "SensorUsername=%s", sensor_username); + } + else { + error_log(LOG_ERROR, NULL, + "Missing mandatory parameter SensorUsername.\n"); + logc_shutdown(1); + } + + s = apr_table_get(conf, "SensorPassword"); + if (s != NULL) { + sensor_password = s; + error_log(LOG_DEBUG2, NULL, "SensorPassword=%s", sensor_password); + } + else { + error_log(LOG_ERROR, NULL, + "Missing mandatory parameter SensorPassword.\n"); + logc_shutdown(1); + } + + s = apr_table_get(conf, "KeepEntries"); + if (s != NULL) { + keep_entries = atoi(s); + } + else { + keep_entries = 0; + } + error_log(LOG_DEBUG2, NULL, "KeepEntries=%d", keep_entries); +} + + +/** + * Clean-up resources before process shutdown. + */ +static void logc_cleanup(void) +{ + curl_global_cleanup(); +} + + +/** + * Shutdown the logger. + */ +static void logc_shutdown(int rc) +{ + /* Tell the threads to shut down. */ + running = 0; + + error_log(LOG_DEBUG, NULL, "Shutting down"); + + /* Wait for the management thread to stop */ + /* ENH: Need a fixed timeout if this never happens */ + while(management_thread_active != 0) { + apr_sleep(10 * 1000); + } + + if (rc == 0) { + error_log(LOG_NOTICE, NULL, + "ModSecurity Audit Log Collector %s terminating normally.", + VERSION); + } + else { + error_log(LOG_NOTICE, NULL, + "ModSecurity Audit Log Collector %s " + "terminating with error %d", VERSION, rc); + } + + if (error_log_fd != NULL) { + apr_file_flush(error_log_fd); + } + + exit(rc); +} + + +/** + * Handle signals. + */ +static int handle_signals(int signum) +{ + switch (signum) { + case SIGINT: + error_log(LOG_NOTICE, NULL, "Caught SIGINT, shutting down."); + logc_shutdown(0); + case SIGTERM: + error_log(LOG_NOTICE, NULL, "Caught SIGTERM, shutting down."); + logc_shutdown(0); +#ifndef WIN32 + case SIGHUP: + error_log(LOG_NOTICE, NULL, "Caught SIGHUP, ignored."); + /* ENH: reload config? */ + return 0; + case SIGALRM: + error_log(LOG_DEBUG, NULL, "Caught SIGALRM, ignored."); + return 0; + case SIGTSTP: + error_log(LOG_DEBUG, NULL, "Caught SIGTSTP, ignored."); + return 0; +#endif /* WIN32 */ + } +#ifndef WIN32 + error_log(LOG_NOTICE, NULL, + "Caught unexpected signal %d: %s", + signum, apr_signal_description_get(signum)); +#else + error_log(LOG_NOTICE, NULL, "Caught unexpected signal %d", signum); +#endif /* WIN32 */ + logc_shutdown(1); + + return 0; /* should never reach */ +} + +#ifdef WIN32 +/** + * This function is invoked by Curl to read the source file on Windows + */ +static size_t curl_readfunction(void *ptr, size_t size, + size_t nmemb, void *stream) +{ + return fread(ptr, size, nmemb, (FILE *)stream); +} +#endif + +/** + * This function is invoked by Curl to read the response + * body. Since we don't care about the response body the function + * pretends it is retrieving data where it isn't. + */ +static size_t curl_writefunction(void *ptr, size_t size, + size_t nmemb, void *stream) +{ + unsigned char *data = (unsigned char *)ptr; + unsigned char *status = (unsigned char *)stream; + + /* Grab the status line text from the first line of output */ + if ((status[0] == 0) && (status[1] == 1)) { + apr_size_t i, j; + int ismsg = 0; + + status[1] = 0; /* reset hidden init flag */ + + for (i = 0, j = 0; i < STATUSBUF_SIZE; i++) { + /* We found a line ending so we are done */ + if ( data[i] == '\r' ) { + break; + } + /* Skip to after the first space (where msg is) */ + if (ismsg < 3) { + if ((ismsg == 1) && !isspace(data[i])) { + ismsg++; + } + else if (isspace(data[i])) { + ismsg++; + } + continue; + } + + /* Copy data (msg) from data to status */ + status[j++] = data[i]; + } + status[j] = '\0'; + urldecode_inplace(status, j); + } + + /* do nothing */ + return (size * nmemb); +} + + +/** + * This function is invoked by Curl whenever it has something + * to say. We forward its messages to the error log at level + * DEBUG or DEBUG2 depending on the verbosity. + */ +static int curl_debugfunction(CURL *curl, curl_infotype infotype, + char *data, size_t datalen, void *ourdata) +{ + apr_size_t i, effectivelen; + apr_thread_t *thread = (apr_thread_t *)ourdata; + + if (error_log_level < LOG_DEBUG) return 0; + + effectivelen = datalen; + for(i = 0; i < datalen; i++) { + if ((data[i] == 0x0a)||(data[i] == 0x0d)) { + effectivelen = i; + break; + } + } + + switch (infotype) { + case CURLINFO_TEXT: + /* More verbose data starts with an indent */ + if (apr_isspace(data[0])) { + char *dataptr = data + 1; + + /* Skip initial whitespace (indent) */ + while ( ((size_t)(dataptr - data) > datalen) + && apr_isspace(*dataptr)) dataptr++; + dataptr++; + error_log(LOG_DEBUG2, thread, "CURL: %s", + _log_escape(apr_thread_pool_get(thread), dataptr, + effectivelen - (dataptr - data))); + } + else { + error_log(LOG_DEBUG, thread, "CURL: %s", + _log_escape(apr_thread_pool_get(thread), data, + effectivelen)); + } + break; + case CURLINFO_HEADER_IN: + error_log(LOG_DEBUG, thread, "CURL: HEADER_IN %s", + _log_escape(apr_thread_pool_get(thread), data, + effectivelen)); + break; + case CURLINFO_HEADER_OUT: + error_log(LOG_DEBUG, thread, "CURL: HEADER_OUT %s", + _log_escape(apr_thread_pool_get(thread), data, + effectivelen)); + break; + case CURLINFO_DATA_IN: + error_log(LOG_DEBUG2, thread, "CURL: DATA_IN %s", + _log_escape(apr_thread_pool_get(thread), data, + effectivelen)); + break; + case CURLINFO_DATA_OUT: + error_log(LOG_DEBUG2, thread, "CURL: DATA_OUT %s", + _log_escape(apr_thread_pool_get(thread), data, + effectivelen)); + break; + default: + /* Ignore anything else */ + break; + } + + return 0; +} + + +/** + * Initialise the necessary resources and structures. + */ +static void logc_init(void) +{ + char errstr[1024]; + apr_status_t rc = 0; + const char *errptr = NULL; + int i, erroffset; + + queue = apr_array_make(pool, 64, sizeof(entry_t *)); + if (queue == NULL) { + error_log(LOG_ERROR, NULL, MEMALLOC_ERROR_MSG); + logc_shutdown(1); + } + + in_progress = apr_hash_make(pool); + if (in_progress == NULL) { + error_log(LOG_ERROR, NULL, MEMALLOC_ERROR_MSG); + logc_shutdown(1); + } + + if ((rc = apr_global_mutex_create(&gmutex, lockfile, + APR_LOCK_DEFAULT, pool)) != APR_SUCCESS) + { + error_log(LOG_ERROR, NULL, "Failed to create global mutex: %s", + apr_strerror(rc, errstr, 1024)); + logc_shutdown(1); + } + + if ((rc = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, + pool)) != APR_SUCCESS) + { + error_log(LOG_ERROR, NULL, "Failed to create thread mutex: %s", + apr_strerror(rc, errstr, 1024)); + logc_shutdown(1); + } + + entry_counter = 1; + + curl_handles = apr_array_make(pool, max_connections, sizeof(CURL *)); + if (curl_handles == NULL) { + error_log(LOG_ERROR, NULL, MEMALLOC_ERROR_MSG); + logc_shutdown(1); + } + + /* Initialise a number of Curl handles. */ + for(i = 0; i < max_connections; i++) { + CURL *curl = NULL; + + /* Create cURL handle. */ + curl = curl_easy_init(); + + /* Pre-configure the handle. */ + curl_easy_setopt(curl, CURLOPT_UPLOAD, TRUE); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *)NULL); + curl_easy_setopt(curl, CURLOPT_URL, console_uri); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); + /* SSLv3 works better overall as some servers have issues with TLS */ + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv3); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, TRUE); + curl_easy_setopt(curl, CURLOPT_HEADER, TRUE); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_writefunction); + + *(CURL **)apr_array_push(curl_handles) = curl; + } + + logline_regex = pcre_compile(logline_pattern, PCRE_CASELESS, + &errptr, &erroffset, NULL); + if (logline_regex == NULL) { + error_log(LOG_ERROR, NULL, + "Failed to compile pattern: %s\n", logline_pattern); + logc_shutdown(1); + } + + requestline_regex = pcre_compile(requestline_pattern, + PCRE_CASELESS, &errptr, &erroffset, NULL); + if (requestline_regex == NULL) { + error_log(LOG_ERROR, NULL, + "Failed to compile pattern: %s\n", requestline_pattern); + logc_shutdown(1); + } +} + + +/** + * HACK: To allow two mlogcs running against a single dataset we use the + * mtime as a flag for deletion. + * + * 1) Check file date. + * 2) If it is KEEP_ENTRIES_REMOVE_TIME, then remove the file. + * 3) Otherwise set the date and let the other mlogc remove it. + */ +static void keep_entries_hack(apr_pool_t *mp, + apr_thread_t *thread, const char *fn) +{ + apr_file_t *f = NULL; + apr_finfo_t finfo; + char errstr[1024]; + apr_status_t rc; + + /* Opening for write as required for exclusive lock */ + if ((rc = apr_file_open(&f, fn, + APR_READ|APR_WRITE|APR_APPEND, + APR_OS_DEFAULT, mp)) != APR_SUCCESS) + { + error_log(LOG_ERROR, thread, + "Could not open \"%s\": %s", + fn, apr_strerror(rc, errstr, 1024)); + return; + } + + if ((rc = apr_file_lock(f, + APR_FLOCK_EXCLUSIVE|APR_FLOCK_NONBLOCK)) != APR_SUCCESS) + { + error_log(LOG_DEBUG2, thread, + "Waiting for lock on \"%s\": %s", + fn, apr_strerror(rc, errstr, 1024)); + if ((rc = apr_file_lock(f, APR_FLOCK_EXCLUSIVE)) != APR_SUCCESS) { + error_log(LOG_ERROR, thread, + "Could not lock \"%s\": %s", + fn, apr_strerror(rc, errstr, 1024)); + apr_file_close(f); + return; + } + } + error_log(LOG_DEBUG2, thread, "Locked: %s", fn); + + /* For testing only */ + TEST_WITH_RAND_SLEEP(2); + + if ((rc = apr_stat(&finfo, fn, APR_FINFO_MIN, mp)) != APR_SUCCESS) { + error_log(LOG_ERROR, thread, + "Could not stat \"%s\": %s", + fn, apr_strerror(rc, errstr, 1024)); + error_log(LOG_DEBUG2, thread, "Unlocked: %s", fn); + apr_file_close(f); + return; + } + + if (error_log_level >= LOG_DEBUG) { + error_log(LOG_DEBUG, thread, + "STAT \"%s\" {" + "uid=%d; gid=%d; size=%" APR_OFF_T_FMT "; " + "csize=%" APR_OFF_T_FMT "; atime=%" APR_TIME_T_FMT "; " + "ctime=%" APR_TIME_T_FMT "; mtime=%" APR_TIME_T_FMT + "}", + fn, finfo.user, finfo.group, finfo.size, + finfo.csize, finfo.atime, finfo.ctime, finfo.mtime); + } + + if (finfo.mtime != KEEP_ENTRIES_REMOVE_TIME) { + error_log(LOG_DEBUG2, thread, "Set mtime: %s", fn); + if ((rc = apr_file_mtime_set(fn, + (apr_time_t)KEEP_ENTRIES_REMOVE_TIME, mp)) + != APR_SUCCESS) + { + error_log(LOG_ERROR, thread, + "Could not set mtime on \"%s\": %s", + fn, apr_strerror(rc, errstr, 1024)); + } + error_log(LOG_DEBUG2, thread, "Unlocked: %s", fn); + apr_file_close(f); + return; + } + + + error_log(LOG_DEBUG, thread, "Removing: %s", fn); + error_log(LOG_DEBUG2, thread, "Unlocked: %s", fn); + apr_file_close(f); + apr_file_remove(fn, mp); +} + + +/** + * Worker thread. Works in a loop, fetching jobs from the queue, + * until the queue is empty or it is otherwise told to quit. + */ +static void * APR_THREAD_FUNC thread_worker(apr_thread_t *thread, void *data) +{ + unsigned int loop_count = 0; + CURL *curl = (CURL *)data; + entry_t **entryptr = NULL; + entry_t *entry = NULL; + apr_status_t rc; + apr_finfo_t finfo; + int capturevector[CAPTUREVECTORSIZE]; + int take_new = 1; + apr_pool_t *tpool; + struct curl_slist *headerlist = NULL; + char curl_error_buffer[CURL_ERROR_SIZE] = ""; + int num_requests = 0; + + /* There is no need to do the sleep if this was an invalid entry + * as the sleep is just to protect flooding the console server + * with rapid requests. With an invalid entry we never hit the + * server, so we should not delay processing the next event. + */ + int nodelay = 0; + + + error_log(LOG_DEBUG, thread, "Worker thread starting."); + + /* Each worker uses its own pool to manage memory. To avoid + * memory leaks the pool is cleared after each processed + * entry. + */ + apr_pool_create(&tpool, thread_pool); + + /* Process jobs in a queue until there are no more jobs to process. */ + for(;;) { + nodelay = 0; + + /* Do we need to shut down? */ + if (running == 0) { + error_log(LOG_DEBUG, thread, "We were told to shut down."); + goto THREAD_SHUTDOWN; + } + + /* Is there a problem with the server? We need + * to shut down if there is. Except that we don't + * want to shut down if we were launched to investigate + * if the server came back online (loop_count will be + * zero in that case). + */ + if ((server_error == 1)&&(loop_count != 0)) { + error_log(LOG_DEBUG, thread, "Shutting down due to server error."); + goto THREAD_SHUTDOWN; + } + + loop_count++; + + /* Get a new entry, but only if we need one. */ + if (take_new) { + error_log(LOG_DEBUG, thread, "Worker fetch locking thread mutex."); + if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mutex))) { + error_log(LOG_DEBUG, thread, + "Worker fetch waiting on thread mutex."); + apr_thread_mutex_lock(mutex); + } + + error_log(LOG_DEBUG, thread, "Worker fetch started."); + + /* Deal with the previous entry. */ + if (entry != NULL) { + error_log(LOG_DEBUG, thread, + "Removing previous entry from storage."); + transaction_log(TXOUT, entry->line); + + /* Remove previous entry from storage. */ + apr_hash_set(in_progress, &entry->id, sizeof(entry->id), NULL); + + /* Release the memory it used to occupy. */ + free((void *)entry->line); + free(entry); + entry = NULL; + } + + error_log(LOG_DEBUG, thread, "Getting one entry from the queue."); + + /* Get one entry. */ + entryptr = (entry_t **)apr_array_pop(queue); + if (entryptr == NULL) { + error_log(LOG_DEBUG, thread, + "Worker fetch unlocking thread mutex."); + apr_thread_mutex_unlock(mutex); + error_log(LOG_DEBUG, thread, + "No more work for this thread, exiting."); + + goto THREAD_SHUTDOWN; + } + + entry = *entryptr; + apr_hash_set(in_progress, &entry->id, sizeof(entry->id), entry); + + error_log(LOG_DEBUG, thread, "Worker fetch completed."); + + error_log(LOG_DEBUG, thread, + "Worker fetch unlocking thread mutex."); + apr_thread_mutex_unlock(mutex); + } + + /* Send one entry. */ + + error_log(LOG_DEBUG, thread, "Processing entry."); + take_new = 0; + + /* Keep track of requests processed if we need to */ + if (max_worker_requests > 0) { + num_requests++; + } + + rc = pcre_exec(logline_regex, NULL, entry->line, entry->line_size, 0, 0, + capturevector, CAPTUREVECTORSIZE); + if (rc == PCRE_ERROR_NOMATCH) { /* No match. */ + error_log(LOG_WARNING, thread, + "Invalid entry (failed to match regex): %s", + _log_escape(tpool, entry->line, entry->line_size)); + take_new = 1; + nodelay = 1; + } + else if (rc < 0) { /* Error condition. */ + error_log(LOG_WARNING, thread, + "Invalid entry (PCRE error %d): %s", + rc, _log_escape(tpool, entry->line, entry->line_size)); + take_new = 1; + nodelay = 1; + } + else { /* We have a match. */ + char *uniqueid = NULL; + char *auditlogentry = NULL; + char *hash = NULL; + char *summary = NULL; + char *credentials = NULL; + + error_log(LOG_DEBUG, thread, "Regular expression matched."); + + /* For testing only */ + TEST_WITH_RAND_SLEEP(2); + + uniqueid = apr_psprintf(tpool, "%.*s", + (capturevector[2*13+1] - capturevector[2*13]), + (entry->line + capturevector[2*13])); + auditlogentry = apr_psprintf(tpool, "%s/%.*s", log_repository, + (capturevector[2*15+1] - capturevector[2*15]), + (entry->line + capturevector[2*15])); + hash = apr_psprintf(tpool, "X-Content-Hash: %.*s", + (capturevector[2*18+1] - capturevector[2*15]), + (entry->line + capturevector[2*18])); + summary = apr_psprintf(tpool, "X-ForensicLog-Summary: %s", + entry->line); + credentials = apr_psprintf(tpool, "%s:%s", + sensor_username, sensor_password); + + rc = apr_stat(&finfo, auditlogentry, APR_FINFO_SIZE, tpool); + if (rc == APR_SUCCESS) { + FILE *hd_src; + char response_buf[STATUSBUF_SIZE]; + CURLcode res; + + if (error_log_level >= LOG_DEBUG) { + error_log(LOG_DEBUG, thread, + "STAT \"%s\" {" + "uid=%d; gid=%d; size=%" APR_OFF_T_FMT "; " + "csize=%" APR_OFF_T_FMT "; " + "atime=%" APR_TIME_T_FMT "; " + "ctime=%" APR_TIME_T_FMT "; " + "mtime=%" APR_TIME_T_FMT + "}", + auditlogentry, finfo.user, finfo.group, + finfo.size, finfo.csize, finfo.atime, + finfo.ctime, finfo.mtime); + } + + /* Initialize the respone buffer with a hidden value */ + response_buf[0] = 0; + response_buf[1] = 1; + + if (finfo.size == 0) { + error_log(LOG_WARNING, thread, + "File found (%" APR_SIZE_T_FMT + " bytes), skipping.", finfo.size); + take_new = 1; + nodelay = 1; + goto THREAD_CLEANUP; + } + else { + error_log(LOG_DEBUG, thread, + "File found (%" APR_SIZE_T_FMT + " bytes), activating cURL.", finfo.size); + } + + + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, + curl_debugfunction); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, thread); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_error_buffer); + curl_easy_setopt(curl, CURLOPT_USERPWD, credentials); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (char *)response_buf); + + headerlist = curl_slist_append(headerlist, "Expect:"); + headerlist = curl_slist_append(headerlist, hash); + headerlist = curl_slist_append(headerlist, summary); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); + + hd_src = fopen(auditlogentry, "rb"); + if (hd_src == NULL) { + error_log(LOG_WARNING, thread, + "Invalid entry (failed to open file for " + "reading): %s", auditlogentry); + take_new = 1; + nodelay = 1; + goto THREAD_CLEANUP; + } + + curl_easy_setopt(curl, CURLOPT_READDATA, hd_src); + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, finfo.size); + +#ifdef WIN32 + /* Mandatory on win32 */ + curl_easy_setopt(curl, CURLOPT_READFUNCTION, curl_readfunction); +#endif + + res = curl_easy_perform(curl); + + fclose(hd_src); + + if (res == 0) { + long response_code = 0; + + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, + &response_code); + error_log(LOG_DEBUG, thread, + "Request returned with status \"%ld %s\": %s", + response_code, response_buf, uniqueid); + + + if (response_code == 0) { + /* Assume problem with connection */ + error_log(LOG_WARNING, thread, + "Flagging server as errored after failure " + "to retrieve response code for entry %s " + "(cURL code %d): Possible SSL negotiation " + "error", + uniqueid, res); + apr_sleep(1000 * 1000); + take_new = 0; + server_error = 1; + server_error_last_check_time = apr_time_now(); + } + else if (res != 0) { + error_log(LOG_WARNING, thread, + "Flagging server as errored after failure " + "to retrieve response code for entry %s " + "(cURL code %d): %s", + uniqueid, res, curl_error_buffer); + apr_sleep(1000 * 1000); + take_new = 0; + server_error = 1; + server_error_last_check_time = apr_time_now(); + } + else { + if (response_code == 200) { + double total_time, upload_size; + + if (server_error == 1) { + error_log(LOG_NOTICE, thread, + "Clearing the server error flag " + "after successful entry " + "submission: %s", uniqueid); + } + server_error = 0; + server_error_last_check_time = 0; + + curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, + &total_time); + curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD, + &upload_size); + + if (!keep_entries) { + error_log(LOG_DEBUG, thread, + "Removing: %s", auditlogentry); + apr_file_remove(auditlogentry, tpool); + } + else if (keep_entries == KEEP_ENTRIES_REMOVE_HACK) + { + keep_entries_hack(tpool, thread, auditlogentry); + } + + error_log(LOG_NOTICE, thread, + "Entry completed (%.3f seconds, %.0f " + "bytes): %s", + total_time, upload_size, + uniqueid); + take_new = 1; + } + else if (response_code == 409) { + /* Assume problem with audit log entry. */ + error_log(LOG_WARNING, thread, + "Failed to submit entry with " + "\"409 %s\": %s", + response_buf, uniqueid); + take_new = 1; + } + else { + /* Assume problem with server. */ + error_log(LOG_WARNING, thread, + "Flagging server as errored after " + "failure to submit entry %s with " + "HTTP response code %ld: %s", + uniqueid, response_code, response_buf); + server_error = 1; + server_error_last_check_time = apr_time_now(); + take_new = 0; + } + } + } + else { /* Something isn't right. */ + error_log(LOG_WARNING, thread, + "Flagging server as errored after " + "failure to submit entry %s " + "(cURL code %d): %s", + uniqueid, res, curl_error_buffer); + server_error = 1; + server_error_last_check_time = apr_time_now(); + take_new = 0; + + } + } + else { + error_log(LOG_WARNING, thread, + "Invalid entry (file not found %d): %s", + rc, auditlogentry); + take_new = 1; + nodelay = 1; + } + + /* If we are tracking num_requests, then shutdown if we are + * over our threshold. + */ + if (num_requests && (num_requests >= max_worker_requests)) { + error_log(LOG_NOTICE, thread, + "Reached max requests (%d) for this worker, exiting.", + max_worker_requests); + + goto THREAD_SHUTDOWN; + } + } + + THREAD_CLEANUP: + + /* Sleep if we sent data to the server so we do not flood */ + /* ENH: Need to sleep for 1ms in a loop checking for shutdown */ + if ((nodelay == 0) && (transaction_delay > 0)) { + error_log(LOG_DEBUG, thread, + "Sleeping for %d msec.", transaction_delay); + apr_sleep(transaction_delay * 1000); + } + + if (headerlist != NULL) { + curl_slist_free_all(headerlist); + headerlist = NULL; + } + + apr_pool_clear(tpool); + + error_log(LOG_DEBUG, thread, "Worker processing completed."); + } + + THREAD_SHUTDOWN: + + error_log(LOG_DEBUG, thread, "Worker shutdown locking thread mutex."); + if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mutex))) { + error_log(LOG_DEBUG, thread, + "Worker shutdown waiting on thread mutex."); + apr_thread_mutex_lock(mutex); + } + + /* Deal with the previous entry, if any. */ + if (entry != NULL) { + apr_hash_set(in_progress, &entry->id, sizeof(entry->id), NULL); + + if (take_new == 0) { /* Not done. */ + *(entry_t **)apr_array_push(queue) = entry; + } + else { + transaction_log(TXOUT, entry->line); + free((void *)entry->line); + free(entry); + } + + entry = NULL; + } + + /* Return curl handle to the pool for reuse. */ + *(CURL **)apr_array_push(curl_handles) = curl; + + /* No more work, exit. */ + current_workers--; + + error_log(LOG_DEBUG, thread, "Worker shutdown unlocking thread mutex."); + apr_thread_mutex_unlock(mutex); + + apr_pool_destroy(tpool); + + error_log(LOG_DEBUG, thread, "Worker thread completed."); + + apr_thread_exit(thread, 0); + + return NULL; +} + + +/** + * Creates one new worker, giving it one of the available + * Curl handles to work with. + */ +static void create_new_worker(int lock) +{ + apr_thread_t *thread = NULL; + CURL **curlptr = NULL; + + if (lock) { + error_log(LOG_DEBUG, NULL, "Worker creation locking thread mutex."); + if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mutex))) { + error_log(LOG_DEBUG, NULL, + "Worker creation waiting on thread mutex."); + apr_thread_mutex_lock(mutex); + } + } + + error_log(LOG_DEBUG, NULL, "Worker creation started."); + + /* A sanity check: this part executes under lock and + * we want to make *sure* we don't create more threads + * than we are allowed. + */ + if (current_workers >= max_connections) { + if (lock) { + error_log(LOG_DEBUG, NULL, + "Worker creation unlocking thread mutex."); + apr_thread_mutex_unlock(mutex); + } + return; + } + + /* Cleanup thread pool when idle */ + if (current_workers <= 0) { + if (thread_pool != NULL) { + error_log(LOG_DEBUG, NULL, "Destroying thread_pool."); + apr_pool_destroy(thread_pool); + thread_pool = NULL; + } + error_log(LOG_DEBUG, NULL, "Creating thread_pool."); + apr_pool_create(&thread_pool, NULL); + } + + curlptr = (CURL **)apr_array_pop(curl_handles); + if (curlptr != NULL) { + apr_threadattr_t *thread_attrs; + apr_status_t rc; + + apr_threadattr_create(&thread_attrs, thread_pool); + apr_threadattr_detach_set(thread_attrs, 1); + apr_threadattr_stacksize_set(thread_attrs, 1024); + + rc = apr_thread_create(&thread, thread_attrs, + thread_worker, *curlptr, thread_pool); + if (rc != APR_SUCCESS) { + if (lock) { + error_log(LOG_DEBUG, NULL, + "Worker creation unlocking thread mutex."); + apr_thread_mutex_unlock(mutex); + } + error_log(LOG_ERROR, NULL, + "Failed to create new worker thread: %d", rc); + logc_shutdown(1); + } + + current_workers++; + } + else { + if (lock) { + error_log(LOG_DEBUG, NULL, + "Worker creation unlocking thread mutex."); + apr_thread_mutex_unlock(mutex); + } + error_log(LOG_ERROR, NULL, "No more cURL handles (Internal Error)."); + logc_shutdown(1); + } + + error_log(LOG_DEBUG, NULL, "Worker creation completed: %pp", thread); + + if (lock) { + error_log(LOG_DEBUG, NULL, "Worker creation unlocking thread mutex."); + apr_thread_mutex_unlock(mutex); + } +} + + +/** + * This function implements the management thread. + */ +static void * APR_THREAD_FUNC thread_manager(apr_thread_t *thread, void *data) +{ + apr_time_t last = 0; + apr_time_t now = 0; + + error_log(LOG_DEBUG, thread, "Management thread: Starting."); + + for(;;) { + now = apr_time_now(); + + /* Should we stop running? */ + if (running == 0) { + /* We need to be last */ + error_log(LOG_DEBUG, thread, + "Management thread: Waiting for worker " + "threads to finish."); + while(current_workers > 0) { + apr_sleep(10 * 1000); + } + + if (have_read_data) { + error_log(LOG_NOTICE, thread, + "Running final transaction checkpoint."); + transaction_checkpoint(); + } + + error_log(LOG_DEBUG, thread, "Management thread: Exiting."); + management_thread_active = 0; + apr_thread_exit(thread, 0); + } + + /* Sleep for a while, but wake up often to check running status */ + if ((last > 0) && ((now - last) < MANAGER_SLEEP)) { + apr_sleep(MANAGER_SUBSLEEP); + continue; + } + last = now; + + error_log(LOG_DEBUG2, thread, "Management thread: Processing"); + + /* When the server is flagged errored we need to + * create a worker thread from time to time to + * investigate. + */ + if (server_error) { + if ((current_workers == 0)&& + (apr_time_sec(now - server_error_last_check_time) + > server_error_timeout)) + { + server_error_last_check_time = now; + error_log(LOG_DEBUG, thread, + "Management thread: Creating worker thread to " + "investigate server."); + create_new_worker(1); + } + } + else { + if ( (current_workers < max_connections) + && (queue->nelts > current_workers) ) + { + error_log(LOG_DEBUG, thread, + "Management thread: Creating worker thread to " + "catch up with the queue."); + create_new_worker(1); + } + } + + /* Initiate a transaction log checkpoint if enough time passed + * since the last one. + */ + if (apr_time_sec(now - checkpoint_time_last) > checkpoint_interval) { + error_log(LOG_DEBUG, thread, + "Management thread: Initiating a checkpoint " + "(previous was %" APR_TIME_T_FMT " seconds ago).", + apr_time_sec(now - checkpoint_time_last)); + checkpoint_time_last = now; + transaction_checkpoint(); + } + else { + error_log(LOG_DEBUG2, thread, + "Management thread: Last checkpoint was %" APR_TIME_T_FMT + " seconds ago.", + apr_time_sec(now - checkpoint_time_last)); + } + } + + return NULL; +} + +#ifndef WIN32 +/** + * Thread to handle all signals + */ +static void * APR_THREAD_FUNC thread_signals(apr_thread_t *thread, void *data) +{ + apr_status_t rc; + + error_log(LOG_DEBUG, thread, "Signal thread: Starting."); + rc = apr_signal_thread(handle_signals); + if (rc != APR_SUCCESS) { + error_log(LOG_DEBUG, thread, "Signal thread: Error %d", rc); + logc_shutdown(1); + } + + return NULL; +} +#endif /* WIN32 */ + +/** + * The main loop where we receive log entries from + * Apache and add them to the queue, sometimes creating + * new worker threads to handle them. + */ +static void receive_loop(void) { + apr_file_t *fd_stdin; + apr_size_t nbytes = PIPE_BUF_SIZE; + char *buf = apr_palloc(pool, PIPE_BUF_SIZE + 1); + char errstr[1024]; + apr_size_t evnt = 0; /* Index in buf to first event char */ + apr_size_t curr = 0; /* Index in buf to current processing char */ + apr_size_t next = 0; /* Index in buf to next unused char */ + int done = 0; + int drop_next = 0; + int buffered_events = 0; + int count = 0; + apr_pool_t *tmp_pool; + + /* Open stdin. */ + if (apr_file_open_stdin(&fd_stdin, pool) != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, "Unable to open stdin for reading"); + logc_shutdown(1); + } + + /* Always want this NUL terminated */ + buf[PIPE_BUF_SIZE] = '\0'; + + apr_pool_create(&tmp_pool, NULL); + + /* Loop forever receiving entries from stdin. */ + while(!done || (curr < next)) { + apr_status_t rc; + + if (error_log_level >= LOG_DEBUG2) { + error_log(LOG_DEBUG2, NULL, + "Internal state: " + "[evnt \"%" APR_SIZE_T_FMT "\"]" + "[curr \"%" APR_SIZE_T_FMT "\"]" + "[next \"%" APR_SIZE_T_FMT "\"]" + "[nbytes \"%" APR_SIZE_T_FMT "\"]", + evnt, curr, next, nbytes); + } + + /* If we are not done and have the space, read more */ + if (!done && (nbytes > 0)) { + buffered_events = 0; + nbytes = PIPE_BUF_SIZE - next; + rc = apr_file_read(fd_stdin, (buf + next), &nbytes); + if (rc != APR_SUCCESS) { + if (have_read_data) { + error_log(LOG_NOTICE, NULL, + "No more data to read, emptying buffer: %s", + apr_strerror(rc, errstr, 1024)); + } + done = 1; + } + else { + have_read_data = 1; + if (error_log_level == LOG_DEBUG) { + error_log(LOG_DEBUG, NULL, + "Read %" APR_SIZE_T_FMT " bytes from pipe", + nbytes); + } + else { + error_log(LOG_DEBUG2, NULL, + "Read %" APR_SIZE_T_FMT " bytes from pipe: `%s'", + nbytes, + _log_escape(tmp_pool, (buf + next), nbytes)); + } + } + + next += nbytes; + } + + /** + * Each chunk of data we receive can contain one or more lines for + * which we need to find the EOL marker and then queue the event + * up to that. So, find/queue as many lines in the buffer as we + * can. Any remaining data will get shifted back to the beginning + * of the buffer and the buffer size for the next read adjusted. + */ + while(curr < next) { + /* Look for EOL so we can parse the event */ + while((curr < next) && (buf[curr] != 0x0a)) { + curr++; + } + if (buf[curr] == 0x0a) { + buf[curr] = '\0'; + + /* We may have to drop this one if it previously failed */ + if (drop_next) { + error_log(LOG_ERROR, NULL, + "Dropping remaining portion of failed " + "event: `%s'", + _log_escape(tmp_pool, + (buf + evnt), (curr - evnt))); + drop_next = 0; + } + else { + transaction_log(TXIN, buf + evnt); + error_log(LOG_DEBUG2, NULL, + "Received audit log entry " + "(count %lu queue %d workers %d): %s", + entry_counter, queue->nelts, + current_workers, + _log_escape(tmp_pool, + (buf + evnt), strlen(buf + evnt))); + add_entry(buf + evnt, 1); + buffered_events++; + } + + /* Advance indexes to next event in buf */ + evnt = curr = curr + 1; + } + else { + error_log(LOG_DEBUG2, NULL, + "Event buffer contains partial event: `%s'", + _log_escape(tmp_pool, (buf + evnt), (next - evnt))); + break; + } + } + + + if (buffered_events > 0) { + error_log(LOG_DEBUG, NULL, + "Processed %d entries from buffer.", buffered_events); + + /* Move the unused portion of the buffer to the beginning */ + next -= evnt; + curr -= evnt; + memmove(buf, (buf + evnt), next); + + error_log(LOG_DEBUG2, NULL, + "Shifted buffer back %" APR_SIZE_T_FMT + " and offset %" APR_SIZE_T_FMT + " bytes for next read: `%s'", + evnt, next, _log_escape(tmp_pool, buf, next)); + + evnt = 0; + } + else if (next == PIPE_BUF_SIZE) { + /** + * There is a chance we could fill the buffer, but not have finished + * reading the event (no EOL yet), so we need to say so and drop + * all data until we find the end of the event that is too large. + */ + + if (drop_next) { + error_log(LOG_ERROR, NULL, + "Event continuation too large, " + "dropping it as well: `%s'", + _log_escape(tmp_pool, buf, PIPE_BUF_SIZE)); + } + else { + error_log(LOG_ERROR, NULL, + "Event too large, dropping event: `%s'", + _log_escape(tmp_pool, buf, PIPE_BUF_SIZE)); + } + + /* Rewind buf and mark that we need to drop up to the next event */ + evnt = curr = next = 0; + drop_next = 1; + } + + nbytes = PIPE_BUF_SIZE - next; + + if (count++ > 1000) { + count = 0; + error_log(LOG_DEBUG, NULL, "Recycling tmp_pool."); + apr_pool_destroy(tmp_pool); + apr_pool_create(&tmp_pool, NULL); + } + else { + apr_pool_clear(tmp_pool); + } + } + + /* Wait for queue to empty if specified */ + if ((server_error == 0) && (opt_force != 0) && (queue->nelts > 0)) { + error_log(LOG_NOTICE, NULL, + "Waiting for queue to empty (%d active).", queue->nelts); + while ((server_error == 0) && (opt_force != 0) && (queue->nelts > 0)) { + apr_sleep(10 * 1000); + } + if (queue->nelts > 0) { + error_log(LOG_ERROR, NULL, + "Could not empty queue (%d active).", queue->nelts); + } + } +} + + +/** + * Creates the management thread. + */ +static void start_management_thread(void) +{ + apr_thread_t *thread = NULL; + apr_threadattr_t *thread_attrs; + apr_status_t rc; + + apr_threadattr_create(&thread_attrs, pool); + apr_threadattr_detach_set(thread_attrs, 1); + apr_threadattr_stacksize_set(thread_attrs, 1024); + + management_thread_active = 1; + + rc = apr_thread_create(&thread, thread_attrs, thread_manager, NULL, pool); + if (rc != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, + "Failed to create new management thread: %d", rc); + management_thread_active = 0; + logc_shutdown(1); + } +} +#ifndef WIN32 +/** + * Creates a thread to handle all signals + */ +static void start_signal_thread(void) +{ + apr_thread_t *thread = NULL; + apr_threadattr_t *thread_attrs; + apr_status_t rc; + + apr_threadattr_create(&thread_attrs, pool); + apr_threadattr_detach_set(thread_attrs, 1); + apr_threadattr_stacksize_set(thread_attrs, 1024); + + rc = apr_thread_create(&thread, thread_attrs, thread_signals, NULL, pool); + if (rc != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, + "Failed to create new signal thread: %d", rc); + logc_shutdown(1); + } +} +#endif /* WIN32 */ + +/** + * Usage text. + */ +static void usage(void) { + fprintf(stderr, "ModSecurity Log Collector (mlogc) v%s\n", VERSION); + fprintf(stderr, " Usage: mlogc [options] /path/to/the/mlogc.conf\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " Options:\n"); + fprintf(stderr, " -f Force depletion of queue on exit\n"); + fprintf(stderr, " -v Version information\n"); + fprintf(stderr, " -h This help\n\n"); +} + +/** + * Version text. + */ +static void version(void) { + fprintf(stderr, + "ModSecurity Log Collector (mlogc) v%s\n", VERSION); + fprintf(stderr, + " APR: compiled=\"%s\"; " + "loaded=\"%s\"\n", APR_VERSION_STRING, apr_version_string()); + fprintf(stderr, + " PCRE: compiled=\"%d.%d\"; " + "loaded=\"%s\"\n", PCRE_MAJOR, PCRE_MINOR, pcre_version()); + fprintf(stderr, + " cURL: compiled=\"%s\"; " + "loaded=\"%s\"\n", LIBCURL_VERSION, curl_version()); + fprintf(stderr, "\n"); +} + +/** + * This is the main entry point. + */ +int main(int argc, const char * const argv[]) { + apr_getopt_t *opt; + apr_status_t rc; + + apr_app_initialize(&argc, &argv, NULL); + atexit(apr_terminate); + + curl_global_init(CURL_GLOBAL_ALL); + atexit(logc_cleanup); + + logc_pid = getpid(); + apr_pool_create(&pool, NULL); + apr_pool_create(&recv_pool, NULL); + +#ifndef WIN32 + apr_setup_signal_thread(); +#else + apr_signal(SIGINT, handle_signals); + apr_signal(SIGTERM, handle_signals); +#endif /* WIN32 */ + + if (argc < 2) { + usage(); + logc_shutdown(1); + } + + /* Commandline opts */ + rc = apr_getopt_init(&opt, pool, argc, argv); + if (rc != APR_SUCCESS) { + usage(); + logc_shutdown(1); + } + + do { + char ch; + const char *val; + rc = apr_getopt(opt, CMDLINE_OPTS, &ch, &val); + switch (rc) { + case APR_SUCCESS: + switch (ch) { + case 'f': + opt_force = 1; + break; + case 'v': + version(); + logc_shutdown(0); + case 'h': + usage(); + logc_shutdown(0); + } + break; + case APR_BADCH: + case APR_BADARG: + usage(); + logc_shutdown(1); + } + } while (rc != APR_EOF); + + /* Conf file is last */ + conffile = argv[argc - 1]; + + read_configuration(); + init_configuration(); + + logc_init(); + transaction_log_init(); + + running = 1; + server_error = 0; + + start_management_thread(); +#ifndef WIN32 + start_signal_thread(); +#endif /* WIN32 */ + + /* Process stdin until EOF */ + receive_loop(); + + logc_shutdown(0); + + return 0; +} diff --git a/2.5.13/2.5.x/apache2/mod_security2.c b/2.5.13/2.5.x/apache2/mod_security2.c new file mode 100644 index 00000000..2c47bb47 --- /dev/null +++ b/2.5.13/2.5.x/apache2/mod_security2.c @@ -0,0 +1,1250 @@ +/* + * 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 + +#include "http_core.h" +#include "http_request.h" +#include "http_connection.h" + +#include "modsecurity.h" +#include "apache2.h" +#include "http_main.h" +#include "pdf_protect.h" + +#include "msc_logging.h" +#include "msc_util.h" + +#include "ap_mpm.h" +#include "scoreboard.h" + +/* ModSecurity structure */ + +msc_engine DSOLOCAL *modsecurity = NULL; + +/* Global module variables; these are used for the Apache-specific functionality */ + +char DSOLOCAL *chroot_dir = NULL; + +unsigned int DSOLOCAL chroot_completed = 0; + +char DSOLOCAL *new_server_signature = NULL; + +char DSOLOCAL *real_server_signature = NULL; + +char DSOLOCAL *guardianlog_name = NULL; + +apr_file_t DSOLOCAL *guardianlog_fd = NULL; + +char DSOLOCAL *guardianlog_condition = NULL; + +unsigned long int DSOLOCAL msc_pcre_match_limit = 0; + +unsigned long int DSOLOCAL msc_pcre_match_limit_recursion = 0; + +unsigned long int DSOLOCAL conn_read_state_limit = 0; + +static int server_limit, thread_limit; + +typedef struct { + int child_num; + int thread_num; +} sb_handle; + +/* -- Miscellaneous functions -- */ + +/** + * Intercepts transaction, using the method specified + * in the structure itself. MUST return an HTTP status code, + * which will be used to terminate the transaction. + */ +int perform_interception(modsec_rec *msr) { + msre_actionset *actionset = NULL; + const char *message = NULL; + const char *phase_text = ""; + int status = DECLINED; + int log_level = 1; + + /* Sanity checks first. */ + + if (msr->was_intercepted == 0) { + msr_log(msr, 1, "Internal Error: Asked to intercept request but was_intercepted is zero"); + return DECLINED; + } + + if (msr->phase > 4) { + msr_log(msr, 1, "Internal Error: Asked to intercept request in phase %d.", msr->phase); + msr->was_intercepted = 0; + return DECLINED; + } + + /* OK, we're good to go. */ + + actionset = msr->intercept_actionset; + phase_text = apr_psprintf(msr->mp, " (phase %d)", msr->phase); + + /* By default we log at level 1 but we switch to 4 + * if a nolog action was used or this is not the initial request + * to hide the message. + */ + log_level = (actionset->log != 1) ? 4 : 1; + + /* Pause the request first (if configured and the initial request). */ + if (actionset->intercept_pause) { + msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for " + "%d msec.", actionset->intercept_pause); + /* apr_sleep accepts microseconds */ + apr_sleep((apr_interval_time_t)(actionset->intercept_pause * 1000)); + } + + /* Determine how to respond and prepare the log message. */ + switch(actionset->intercept_action) { + case ACTION_DENY : + if (actionset->intercept_status != 0) { + status = actionset->intercept_status; + message = apr_psprintf(msr->mp, "Access denied with code %d%s.", + status, phase_text); + } else { + log_level = 1; + status = HTTP_INTERNAL_SERVER_ERROR; + message = apr_psprintf(msr->mp, "Access denied with code 500%s " + "(Internal Error: Invalid status code requested %d).", + phase_text, actionset->intercept_status); + } + break; + + case ACTION_PROXY : + if (msr->phase < 3) { + if (ap_find_linked_module("mod_proxy.c") == NULL) { + log_level = 1; + status = HTTP_INTERNAL_SERVER_ERROR; + message = apr_psprintf(msr->mp, "Access denied with code 500%s " + "(Configuration Error: Proxy action to %s requested but mod_proxy not found).", + phase_text, + log_escape_nq(msr->mp, actionset->intercept_uri)); + } else { + msr->r->filename = apr_psprintf(msr->mp, "proxy:%s", actionset->intercept_uri); + msr->r->proxyreq = PROXYREQ_REVERSE; + msr->r->handler = "proxy-server"; + status = OK; + message = apr_psprintf(msr->mp, "Access denied using proxy to%s %s.", + phase_text, + log_escape_nq(msr->mp, actionset->intercept_uri)); + } + } else { + log_level = 1; + status = HTTP_INTERNAL_SERVER_ERROR; + message = apr_psprintf(msr->mp, "Access denied with code 500%s " + "(Configuration Error: Proxy action requested but it does not work in output phases).", + phase_text); + } + break; + + case ACTION_DROP : + /* ENH This does not seem to work on Windows. Is there a + * better way to drop a connection anyway? + */ + #ifndef WIN32 + { + extern module core_module; + apr_socket_t *csd = ap_get_module_config(msr->r->connection->conn_config, + &core_module); + + if (csd) { + if (apr_socket_close(csd) == APR_SUCCESS) { + status = HTTP_FORBIDDEN; + message = apr_psprintf(msr->mp, "Access denied with connection close%s.", + phase_text); + } else { + log_level = 1; + status = HTTP_INTERNAL_SERVER_ERROR; + message = apr_psprintf(msr->mp, "Access denied with code 500%s " + "(Error: Connection drop requested but failed to close the " + " socket).", + phase_text); + } + } else { + log_level = 1; + status = HTTP_INTERNAL_SERVER_ERROR; + message = apr_psprintf(msr->mp, "Access denied with code 500%s " + "(Error: Connection drop requested but socket not found.", + phase_text); + } + } + #else + log_level = 1; + status = HTTP_INTERNAL_SERVER_ERROR; + message = apr_psprintf(msr->mp, "Access denied with code 500%s " + "(Error: Connection drop not implemented on this platform).", + phase_text); + #endif + break; + + case ACTION_REDIRECT : + apr_table_setn(msr->r->headers_out, "Location", actionset->intercept_uri); + if ((actionset->intercept_status == 301)||(actionset->intercept_status == 302) + ||(actionset->intercept_status == 303)||(actionset->intercept_status == 307)) + { + status = actionset->intercept_status; + } else { + status = HTTP_MOVED_TEMPORARILY; + } + message = apr_psprintf(msr->mp, "Access denied with redirection to %s using " + "status %d%s.", + log_escape_nq(msr->mp, actionset->intercept_uri), status, + phase_text); + break; + + case ACTION_ALLOW : + status = DECLINED; + message = apr_psprintf(msr->mp, "Access allowed%s.", phase_text); + msr->was_intercepted = 0; + msr->allow_scope = ACTION_ALLOW; + break; + + case ACTION_ALLOW_PHASE : + status = DECLINED; + message = apr_psprintf(msr->mp, "Access to phase allowed%s.", phase_text); + msr->was_intercepted = 0; + msr->allow_scope = ACTION_ALLOW_PHASE; + break; + + case ACTION_ALLOW_REQUEST : + status = DECLINED; + message = apr_psprintf(msr->mp, "Access to request allowed%s.", phase_text); + msr->was_intercepted = 0; + msr->allow_scope = ACTION_ALLOW_REQUEST; + break; + + default : + log_level = 1; + status = HTTP_INTERNAL_SERVER_ERROR; + message = apr_psprintf(msr->mp, "Access denied with code 500%s " + "(Internal Error: invalid interception action %d).", + phase_text, actionset->intercept_action); + break; + } + + /* If the level is not high enough to add an alert message, but "auditlog" + * is enabled, then still add the message. */ + if ((log_level > 3) && (actionset->auditlog != 0)) { + *(const char **)apr_array_push(msr->alerts) = msc_alert_message(msr, actionset, NULL, message); + } + + /* Log the message now. */ + msc_alert(msr, log_level, actionset, message, msr->intercept_message); + + /* However, this will mark the txn relevant again if it is <= 3, + * which will mess up noauditlog. We need to compensate for this + * so that we do not increment twice when auditlog is enabled and + * prevent incrementing when auditlog is disabled. + */ + if ((actionset->auditlog == 0) && (log_level <= 3)) { + msr->is_relevant--; + } + + return status; +} + +/** + * Retrieves a previously stored transaction context by + * looking at the main request, and the previous requests. + */ +static modsec_rec *retrieve_tx_context(request_rec *r) { + modsec_rec *msr = NULL; + request_rec *rx = NULL; + + /* Look in the current request first. */ + msr = (modsec_rec *)apr_table_get(r->notes, NOTE_MSR); + if (msr != NULL) { + msr->r = r; + return msr; + } + + /* If this is a subrequest then look in the main request. */ + if (r->main != NULL) { + msr = (modsec_rec *)apr_table_get(r->main->notes, NOTE_MSR); + if (msr != NULL) { + msr->r = r; + return msr; + } + } + + /* If the request was redirected then look in the previous requests. */ + rx = r->prev; + while(rx != NULL) { + msr = (modsec_rec *)apr_table_get(rx->notes, NOTE_MSR); + if (msr != NULL) { + msr->r = r; + return msr; + } + rx = rx->prev; + } + + return NULL; +} + +/** + * Stores transaction context where it can be found in subsequent + * phases, redirections, or subrequests. + */ +static void store_tx_context(modsec_rec *msr, request_rec *r) { + apr_table_setn(r->notes, NOTE_MSR, (void *)msr); +} + +/** + * Creates a new transaction context. + */ +static modsec_rec *create_tx_context(request_rec *r) { + apr_allocator_t *allocator = NULL; + modsec_rec *msr = NULL; + + msr = (modsec_rec *)apr_pcalloc(r->pool, sizeof(modsec_rec)); + if (msr == NULL) return NULL; + + apr_allocator_create(&allocator); + apr_allocator_max_free_set(allocator, 1024); + apr_pool_create_ex(&msr->mp, r->pool, NULL, allocator); + if (msr->mp == NULL) return NULL; + apr_allocator_owner_set(allocator, msr->mp); + + msr->modsecurity = modsecurity; + msr->r = r; + msr->r_early = r; + msr->request_time = r->request_time; + msr->dcfg1 = (directory_config *)ap_get_module_config(r->per_dir_config, + &security2_module); + + /** + * Create a special user configuration. This is where + * explicit instructions will be stored. This will be used + * to override the default settings (and to override the + * configuration in the second phase, dcfg2, with the user + * setting executed in the first phase. + */ + msr->usercfg = create_directory_config(msr->mp, NULL); + if (msr->usercfg == NULL) return NULL; + + /* Create a transaction context and populate + * it using the directory config we just + * got from Apache. + */ + msr->txcfg = create_directory_config(msr->mp, NULL); + if (msr->txcfg == NULL) return NULL; + + if (msr->dcfg1 != NULL) { + msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->dcfg1); + if (msr->txcfg == NULL) return NULL; + } + init_directory_config(msr->txcfg); + + msr->txid = get_env_var(r, "UNIQUE_ID"); + if (msr->txid == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server, + "ModSecurity: ModSecurity requires mod_unique_id to be installed."); + return NULL; + } + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Initialising transaction (txid %s).", msr->txid); + } + + /* Populate tx fields */ + msr->error_messages = apr_array_make(msr->mp, 5, sizeof(error_message *)); + msr->alerts = apr_array_make(msr->mp, 5, sizeof(char *)); + + msr->server_software = real_server_signature; + msr->local_addr = r->connection->local_ip; + msr->local_port = r->connection->local_addr->port; + + msr->remote_addr = r->connection->remote_ip; + msr->remote_port = r->connection->remote_addr->port; + + msr->request_line = r->the_request; + msr->request_uri = r->uri; + msr->request_method = r->method; + msr->query_string = r->args; + msr->request_protocol = r->protocol; + msr->request_headers = apr_table_copy(msr->mp, r->headers_in); + msr->hostname = ap_get_server_name(r); + + msr->msc_rule_mptmp = NULL; + + /* Invoke the engine to continue with initialisation */ + if (modsecurity_tx_init(msr) < 0) { + msr_log(msr, 1, "Failed to initialise transaction (txid %s).", msr->txid); + return NULL; + } + + store_tx_context(msr, r); + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Transaction context created (dcfg %pp).", msr->dcfg1); + } + + return msr; +} + + +/* -- Hooks -- */ + +/* + * Change the signature of the server if change was requested in + * configuration. We do this by locating the signature in server + * memory and writing over it. + */ +static apr_status_t change_server_signature(server_rec *s) { + char *server_version = NULL; + + if (new_server_signature == NULL) return 0; + + server_version = (char *)apache_get_server_version(); + + if (server_version == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, + "SecServerSignature: Apache returned null as signature."); + return -1; + } + + if (strlen(server_version) >= strlen(new_server_signature)) { + strcpy(server_version, new_server_signature); + } + else { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, + "SecServerSignature: original signature too short. Please set " + "ServerTokens to Full."); + return -1; + } + + /* Check that it really changed. This assumes that what we got was + * not a copy and this could change in future versions of Apache. + */ + server_version = (char *)apache_get_server_version(); + if ((server_version == NULL) || (strcmp(server_version, new_server_signature) != 0)) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, "SecServerSignature: Failed to change server signature to \"%s\".", new_server_signature); + return 0; + } + else { + ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, s, "SecServerSignature: Changed server signature to \"%s\".", server_version); + } + + return 1; +} + +/** + * Executed at the end of server lifetime to cleanup the allocated resources. + */ +static apr_status_t module_cleanup(void *data) { + modsecurity_shutdown(modsecurity); + return APR_SUCCESS; +} + +/** + * Pre-configuration initialisation hook. + */ +static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp) { + /* Initialise ModSecurity engine */ + modsecurity = modsecurity_create(mp, MODSEC_ONLINE); + if (modsecurity == NULL) { + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "ModSecurity: Failed to initialise engine."); + return HTTP_INTERNAL_SERVER_ERROR; + } + + return OK; +} + +/** + * Main (post-configuration) module initialisation. + */ +static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp, server_rec *s) { + void *init_flag = NULL; + int first_time = 0; + + /* ENH Figure out a way to validate config before we start + * - skipafter: need to make sure we found all of our targets + */ + + /* Figure out if we are here for the first time */ + apr_pool_userdata_get(&init_flag, "modsecurity-init-flag", s->process->pool); + if (init_flag == NULL) { + first_time = 1; + apr_pool_userdata_set((const void *)1, "modsecurity-init-flag", + apr_pool_cleanup_null, s->process->pool); + } else { + modsecurity_init(modsecurity, mp); + } + + /* Store the original server signature */ + real_server_signature = apr_pstrdup(mp, apache_get_server_version()); + + /* Make some space in the server signature for later */ + if (new_server_signature != NULL) { + ap_add_version_component(mp, new_server_signature); + change_server_signature(s); + } + + #if (!(defined(WIN32) || defined(NETWARE))) + + /* Internal chroot functionality */ + + if (chroot_dir != NULL) { + + /* ENH Is it safe to simply return with an error, instead + * of using exit()? + */ + + if (first_time == 0) { + ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, + "ModSecurity: chroot checkpoint #2 (pid=%ld ppid=%ld)", (long)getpid(), (long)getppid()); + + if (chdir(chroot_dir) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, + "ModSecurity: chroot failed, unable to chdir to %s, errno=%d (%s)", + chroot_dir, errno, strerror(errno)); + exit(1); + } + + if (chroot(chroot_dir) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, + "ModSecurity: chroot failed, path=%s, errno=%d(%s)", + chroot_dir, errno, strerror(errno)); + exit(1); + } + + if (chdir("/") < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, + "ModSecurity: chdoot failed, unable to chdir to /, errno=%d (%s)", + errno, strerror(errno)); + exit(1); + } + + chroot_completed = 1; + + ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, + "ModSecurity: chroot successful, path=%s", chroot_dir); + } else { + ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, + "ModSecurity: chroot checkpoint #1 (pid=%ld ppid=%ld)", (long)getpid(), (long)getppid()); + } + } + #endif + + /* Schedule main cleanup for later, when the main pool is destroyed. */ + apr_pool_cleanup_register(mp, (void *)s, module_cleanup, apr_pool_cleanup_null); + + /* Log our presence to the error log. */ + if (first_time) { + ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, + "%s configured.", MODSEC_MODULE_NAME_FULL); + + /* If we've changed the server signature make note of the original. */ + if (new_server_signature != NULL) { + ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, + "Original server signature: %s", real_server_signature); + } + } + + srand((unsigned int)(time(NULL) * getpid())); + + return OK; +} + +/** + * Initialisation performed for every new child process. + */ +static void hook_child_init(apr_pool_t *mp, server_rec *s) { + modsecurity_child_init(modsecurity); +} + +/** + * Initial request processing, executed immediatelly after + * Apache receives the request headers. + */ +static int hook_request_early(request_rec *r) { + modsec_rec *msr = NULL; + int rc; + + /* This function needs to run only once per transaction + * (i.e. subrequests and redirects are excluded). + */ + if ((r->main != NULL)||(r->prev != NULL)) { + return DECLINED; + } + + /* Initialise transaction context and + * create the initial configuration. + */ + msr = create_tx_context(r); + if (msr == NULL) return DECLINED; + + /* Are we allowed to continue? */ + if (msr->txcfg->is_enabled == MODSEC_DISABLED) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Processing disabled, skipping (hook request_early)."); + } + return DECLINED; + } + + /* Process phase REQUEST_HEADERS */ + rc = DECLINED; + if (modsecurity_process_phase(msr, PHASE_REQUEST_HEADERS) > 0) { + rc = perform_interception(msr); + } + + if ( (msr->txcfg->is_enabled != MODSEC_DISABLED) + && (msr->txcfg->reqbody_access == 1) + && (rc == DECLINED)) + { + /* Check request body limit (non-chunked requests only). */ + if (msr->request_content_length > msr->txcfg->reqbody_limit) { + msr_log(msr, 1, "Request body (Content-Length) is larger than the " + "configured limit (%ld).", msr->txcfg->reqbody_limit); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + } + + return rc; +} + +/** + * Invoked as the first hook in the handler chain, this function + * executes the second phase of ModSecurity request processing. + */ +static int hook_request_late(request_rec *r) { + char *my_error_msg = NULL; + modsec_rec *msr = NULL; + int rc; + + /* This function needs to run only once per transaction + * (i.e. subrequests and redirects are excluded). + */ + if ((r->main != NULL)||(r->prev != NULL)) { + return DECLINED; + } + + /* Find the transaction context and make sure + * we are supposed to proceed. + */ + msr = retrieve_tx_context(r); + if (msr == NULL) { + /* If we can't find the context that probably means it's + * a subrequest that was not initiated from the outside. + */ + return DECLINED; + } + + /* Has this phase been completed already? */ + if (msr->phase_request_body_complete) { + msr_log(msr, 1, "Internal Error: Attempted to process the request body more than once."); + return DECLINED; + } + msr->phase_request_body_complete = 1; + + msr->remote_user = r->user; + + /* Get the second configuration context. */ + msr->dcfg2 = (directory_config *)ap_get_module_config(r->per_dir_config, + &security2_module); + + /* Create a transaction context. */ + msr->txcfg = create_directory_config(msr->mp, NULL); + if (msr->txcfg == NULL) return DECLINED; + if (msr->dcfg2 != NULL) { + msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->dcfg2); + if (msr->txcfg == NULL) return DECLINED; + } + + /* Update with the explicit user settings. */ + msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->usercfg); + + init_directory_config(msr->txcfg); + + /* Perform the PDF tests now. */ + rc = pdfp_check(msr); + if (rc > 0) { + /* The PDF protection module has decided it needs to + * redirect the current transaction. So we let it do that. + */ + return rc; + } + + if (msr->txcfg->is_enabled == 0) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Processing disabled, skipping (hook request_late)."); + } + return DECLINED; + } + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Second phase starting (dcfg %pp).", msr->dcfg2); + } + + /* Figure out whether or not to extract multipart files. */ + if ((msr->txcfg->upload_keep_files != KEEP_FILES_OFF) /* user might want to keep them */ + || (msr->txcfg->upload_validates_files)) /* user might want to validate them */ + { + msr->upload_extract_files = 1; + msr->upload_remove_files = 1; + } + + rc = read_request_body(msr, &my_error_msg); + if (rc < 0) { + switch(rc) { + case -1 : + if (my_error_msg != NULL) { + msr_log(msr, 1, "%s", my_error_msg); + } + return HTTP_INTERNAL_SERVER_ERROR; + break; + case -4 : /* Timeout. */ + if (my_error_msg != NULL) { + msr_log(msr, 4, "%s", my_error_msg); + } + r->connection->keepalive = AP_CONN_CLOSE; + return HTTP_REQUEST_TIME_OUT; + break; + case -5 : /* Request body limit reached. */ + if (my_error_msg != NULL) { + msr_log(msr, 1, "%s", my_error_msg); + } + r->connection->keepalive = AP_CONN_CLOSE; + return HTTP_REQUEST_ENTITY_TOO_LARGE; + break; + default : + /* allow through */ + break; + } + + msr->msc_reqbody_error = 1; + msr->msc_reqbody_error_msg = my_error_msg; + } + + /* Update the request headers. They might have changed after + * the body was read (trailers). + */ + /* NOTE We still need to keep a copy of the original headers + * to log in the audit log. + */ + msr->request_headers = apr_table_copy(msr->mp, r->headers_in); + + /* Process phase REQUEST_BODY */ + record_time_checkpoint(msr, 1); + + rc = DECLINED; + if (modsecurity_process_phase(msr, PHASE_REQUEST_BODY) > 0) { + rc = perform_interception(msr); + } + + record_time_checkpoint(msr, 2); + + return rc; +} + +/** + * Invoked every time Apache has something to write to the error log. + */ +static void hook_error_log(const char *file, int line, int level, apr_status_t status, + const server_rec *s, const request_rec *r, apr_pool_t *mp, const char *fmt) +{ + modsec_rec *msr = NULL; + error_message *em = NULL; + + if (r == NULL) return; + msr = retrieve_tx_context((request_rec *)r); + + /* Create a context for requests we never had the chance to process */ + if ((msr == NULL) + && ((level & APLOG_LEVELMASK) < APLOG_DEBUG) + && apr_table_get(r->subprocess_env, "UNIQUE_ID")) + { + msr = create_tx_context((request_rec *)r); + if (msr->txcfg->debuglog_level >= 9) { + if (msr == NULL) { + msr_log(msr, 9, "Failed to create context after request failure."); + } + else { + msr_log(msr, 9, "Context created after request failure."); + } + } + } + + if (msr == NULL) return; + + /* Store the error message for later */ + em = (error_message *)apr_pcalloc(msr->mp, sizeof(error_message)); + if (em == NULL) return; + + if (file != NULL) em->file = apr_pstrdup(msr->mp, file); + em->line = line; + em->level = level; + em->status = status; + if (fmt != NULL) em->message = apr_pstrdup(msr->mp, fmt); + + /* Remove \n from the end of the message */ + if (em->message != NULL) { + char *p = (char *)em->message; + while(*p != '\0') { + if ((*(p + 1) == '\0')&&(*p == '\n')) { + *p = '\0'; + break; + } + p++; + } + } + + *(const error_message **)apr_array_push(msr->error_messages) = em; +} + +/** + * Guardian logger is used to interface to the external + * script for web server protection - httpd_guardian. + */ +static void sec_guardian_logger(request_rec *r, request_rec *origr, modsec_rec *msr) { + char *str1, *str2, *text; + char *modsec_message = "-"; + int modsec_rating = 0; /* not used yet */ + apr_size_t nbytes, nbytes_written; + apr_time_t duration = (apr_time_now() - origr->request_time); + int limit, was_limited; + + /* bail out if we do not have where to write */ + if ((guardianlog_name == NULL)||(guardianlog_fd == NULL)) return; + + /* process the condition, if we have one */ + if (guardianlog_condition != NULL) { + if (*guardianlog_condition == '!') { + if (apr_table_get(r->subprocess_env, guardianlog_condition + 1) != NULL) { + return; + } + } + else { + if (apr_table_get(r->subprocess_env, guardianlog_condition) == NULL) { + return; + } + } + } + + /* + * Log format is as follows: + * + * %V %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i" %{UNIQUE_ID}e + * "SESSION_ID" %T %D "MODSEC_MESSAGE" MODSEC_RATING + * + * The fields SESSION_ID, MODSEC_MESSAGE, and MODSEC_RATING are not used at the moment. + */ + + str2 = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT " %" APR_TIME_T_FMT " \"%s\" %d", + duration, apr_time_sec(duration), log_escape(msr->mp, modsec_message), modsec_rating); + if (str2 == NULL) return; + + /* We do not want the index line to be longer than 3980 bytes. */ + limit = 3980; + was_limited = 0; + + /* If we are logging to a pipe we need to observe and + * obey the pipe atomic write limit - PIPE_BUF. For + * more details see the discussion in sec_guardian_logger, + * above. + */ + if (msr->txcfg->auditlog_name[0] == '|') { + if (PIPE_BUF < limit) { + limit = PIPE_BUF; + } + } + + limit = limit - strlen(str2) - 5; + if (limit <= 0) { + msr_log(msr, 1, "Audit Log: Atomic PIPE write buffer too small: %d", PIPE_BUF); + return; + } + + str1 = construct_log_vcombinedus_limited(msr, limit, &was_limited); + if (str1 == NULL) return; + + if (was_limited == 0) { + text = apr_psprintf(msr->mp, "%s %s \n", str1, str2); + } else { + text = apr_psprintf(msr->mp, "%s %s L\n", str1, str2); + } + if (text == NULL) return; + + nbytes = strlen(text); + apr_file_write_full(guardianlog_fd, text, nbytes, &nbytes_written); +} + +/** + * Invoked at the end of each transaction. + */ +static int hook_log_transaction(request_rec *r) { + const apr_array_header_t *arr = NULL; + request_rec *origr = NULL; + modsec_rec *msr = NULL; + + msr = retrieve_tx_context(r); + if (msr == NULL) { + return DECLINED; + } + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Initialising logging."); + } + + /* Find the first (origr) and the last (r) request */ + origr = r; + while(origr->prev) { + origr = origr->prev; + } + while(r->next) { + r = r->next; + } + + /* At this point r is the last request in the + * chain. However, we now need to detect a case when + * a bad ErrorDocument was used and back out of it. That's + * how Apache does it internally. Except where Apache knows + * exactly what is happening we will have to rely on the missing + * headers in the final request to detect this condition. + */ + arr = apr_table_elts(r->headers_out); + while ((arr->nelts == 0)&&(r->prev != NULL)) { + r = r->prev; + arr = apr_table_elts(r->headers_out); + } + + msr->r = r; + msr->response_status = r->status; + msr->status_line = ((r->status_line != NULL) + ? r->status_line : ap_get_status_line(r->status)); + msr->response_protocol = get_response_protocol(origr); + msr->response_headers = apr_table_copy(msr->mp, r->headers_out); + if (!r->assbackwards) msr->response_headers_sent = 1; + msr->bytes_sent = r->bytes_sent; + msr->local_user = r->user; + msr->remote_user = r->connection->remote_logname; + + /* -- Guardian -- */ + + sec_guardian_logger(r, origr, msr); + + /* Invoke the engine to do the rest of the work now. */ + modsecurity_process_phase(msr, PHASE_LOGGING); + + return DECLINED; +} + +/** + * Invoked right before request processing begins. This is + * when we need to decide if we want to hook into the output + * filter chain. + */ +static void hook_insert_filter(request_rec *r) { + modsec_rec *msr = NULL; + + /* Find the transaction context first. */ + msr = retrieve_tx_context(r); + if (msr == NULL) return; + + /* Add the input filter, but only if we need it to run. */ + if (msr->if_status == IF_STATUS_WANTS_TO_RUN) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Hook insert_filter: Adding input forwarding filter %s(r %pp).", + (((r->main != NULL)||(r->prev != NULL)) ? "for subrequest " : ""), r); + } + + ap_add_input_filter("MODSECURITY_IN", msr, r, r->connection); + } + + /* The output filters only need to be added only once per transaction + * (i.e. subrequests and redirects are excluded). + */ + if ((r->main != NULL)||(r->prev != NULL)) { + return; + } + + /* We always add the PDF XSS protection filter. */ + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Hook insert_filter: Adding PDF XSS protection output filter (r %pp).", r); + } + + ap_add_output_filter("PDFP_OUT", msr, r, r->connection); + + /* Only proceed to add the second filter if the engine is enabled. */ + if (msr->txcfg->is_enabled == 0) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Hook insert_filter: Processing disabled, skipping."); + } + + return; + } + + /* We always add the output filter because that's where we need to + * initiate our 3rd and 4th processing phases from. The filter is + * smart enough not to buffer the data if it is not supposed to. + */ + if (msr->of_status != OF_STATUS_COMPLETE) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Hook insert_filter: Adding output filter (r %pp).", r); + } + + ap_add_output_filter("MODSECURITY_OUT", msr, r, r->connection); + } +} + +// TODO: Holding off on this for now (needs more testing) +#if 0 +/** + * Invoked whenever Apache starts processing an error. A chance + * to insert ourselves into the output filter chain. + */ +static void hook_insert_error_filter(request_rec *r) { + modsec_rec *msr = NULL; + + /* Find the transaction context and make sure we are + * supposed to proceed. + */ + msr = retrieve_tx_context(r); + if (msr == NULL) return; + + /* Do not run if not enabled. */ + if (msr->txcfg->is_enabled == 0) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Hook insert_error_filter: Processing disabled, skipping."); + } + return; + } + + /* Do not run if the output filter already completed. This will + * happen if we intercept in phase 4. + */ + if (msr->of_status != OF_STATUS_COMPLETE) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Hook insert_error_filter: Adding output filter (r %pp).", r); + } + + /* Make a note that the output we will be receiving is a + * result of error processing. + */ + msr->of_is_error = 1; + + ap_add_output_filter("MODSECURITY_OUT", msr, r, r->connection); + } else { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Hook insert_error_filter: Output buffering already complete."); + } + } +} +#endif + +#if (!defined(NO_MODSEC_API)) +/** + * This function is exported for other Apache modules to + * register new transformation functions. + */ +static void modsec_register_tfn(const char *name, void *fn) { + if (modsecurity != NULL) { + msre_engine_tfn_register(modsecurity->msre, name, (fn_tfn_execute_t)fn); + } +} + +/** + * This function is exported for other Apache modules to + * register new operators. + */ +static void modsec_register_operator(const char *name, void *fn_init, void *fn_exec) { + if (modsecurity != NULL) { + msre_engine_op_register(modsecurity->msre, name, (fn_op_param_init_t)fn_init, (fn_op_execute_t)fn_exec); + } +} + +/* +* \brief Connetion hook to limit the number of +* connections in BUSY state +* +* \param conn Pointer to connection struct +* +* \retval DECLINED On failure +* \retval OK On Success +*/ +static int hook_connection_early(conn_rec *conn) +{ + sb_handle *sb = conn->sbh; + int i, j; + unsigned long int ip_count = 0; + worker_score *ws_record = NULL; + + if(sb != NULL && conn_read_state_limit > 0) { + + ws_record = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num]; + if(ws_record == NULL) + return DECLINED; + + apr_cpystrn(ws_record->client, conn->remote_ip, sizeof(ws_record->client)); + + for (i = 0; i < server_limit; ++i) { + for (j = 0; j < thread_limit; ++j) { + + ws_record = ap_get_scoreboard_worker(i, j); + + if(ws_record == NULL) + return DECLINED; + + switch (ws_record->status) { + case SERVER_BUSY_READ: + if (strcmp(conn->remote_ip, ws_record->client) == 0) + ip_count++; + break; + default: + break; + } + } + } + + if (ip_count > conn_read_state_limit) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "ModSecurity: Access denied with code 400. Too many threads [%ld] of %ld allowed in READ state from %s - Possible DoS Consumption Attack [Rejected]", ip_count,conn_read_state_limit,conn->remote_ip); + return OK; + } else { + return DECLINED; + } + } + + return DECLINED; +} + +/** + * This function is exported for other Apache modules to + * register new variables. + */ +static void modsec_register_variable(const char *name, unsigned int type, + unsigned int argc_min, unsigned int argc_max, + void *fn_validate, void *fn_generate, + unsigned int is_cacheable, unsigned int availability) { + if (modsecurity != NULL) { + msre_engine_variable_register(modsecurity->msre, name, type, argc_min, argc_max, (fn_var_validate_t)fn_validate, (fn_var_generate_t)fn_generate, is_cacheable, availability); + } + else { + fprintf(stderr,"modsecurity is NULL\n"); + } +} +#endif + +/** + * Registers module hooks with Apache. + */ +static void register_hooks(apr_pool_t *mp) { + static const char *postconfig_beforeme_list[] = { + "mod_unique_id.c", + "mod_ssl.c", + NULL + }; + static const char *postconfig_afterme_list[] = { + "mod_fcgid.c", + "mod_cgid.c", + NULL + }; + static const char *postread_beforeme_list[] = { + "mod_rpaf.c", + "mod_rpaf-2.0.c", + "mod_extract_forwarded2.c", + "mod_remoteip.c", + "mod_custom_header.c", + "mod_breach_realip.c", + "mod_breach_trans.c", + "mod_unique_id.c", + NULL + }; + static const char *postread_afterme_list[] = { + "mod_log_forensic.c", + NULL + }; + + /* Add the MODSEC_a.b define */ + *(char **)apr_array_push(ap_server_config_defines) = apr_psprintf(mp, "MODSEC_%s.%s", MODSEC_VERSION_MAJOR, MODSEC_VERSION_MINOR); + +#if (!defined(NO_MODSEC_API)) + /* Export optional functions. */ + APR_REGISTER_OPTIONAL_FN(modsec_register_tfn); + APR_REGISTER_OPTIONAL_FN(modsec_register_operator); + APR_REGISTER_OPTIONAL_FN(modsec_register_variable); +#endif + + /* For connection level hook */ + ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit); + ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit); + + /* Main hooks */ + ap_hook_pre_config(hook_pre_config, NULL, NULL, APR_HOOK_FIRST); + ap_hook_post_config(hook_post_config, postconfig_beforeme_list, + postconfig_afterme_list, APR_HOOK_REALLY_LAST); + ap_hook_child_init(hook_child_init, NULL, NULL, APR_HOOK_MIDDLE); + + /* Our own hook to handle RPC transactions (not used at the moment). + * // ap_hook_handler(hook_handler, NULL, NULL, APR_HOOK_MIDDLE); + */ + + /* Connection processing hooks */ + ap_hook_process_connection(hook_connection_early, NULL, NULL, APR_HOOK_FIRST); + + /* Transaction processing hooks */ + ap_hook_post_read_request(hook_request_early, + postread_beforeme_list, postread_afterme_list, APR_HOOK_REALLY_FIRST); + + ap_hook_fixups(hook_request_late, NULL, NULL, APR_HOOK_REALLY_FIRST); + + /* Logging */ + ap_hook_error_log(hook_error_log, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_log_transaction(hook_log_transaction, NULL, NULL, APR_HOOK_MIDDLE); + + /* Filter hooks */ + ap_hook_insert_filter(hook_insert_filter, NULL, NULL, APR_HOOK_FIRST); +#if 0 + ap_hook_insert_error_filter(hook_insert_error_filter, NULL, NULL, APR_HOOK_FIRST); +#endif + + ap_register_input_filter("MODSECURITY_IN", input_filter, + NULL, AP_FTYPE_CONTENT_SET); + + /* Ensure that the output filter runs before other modules so that + * we get a request that has a better chance of not being modified: + * + * Currently: + * mod_expires = -2 + * mod_cache = -1 + * mod_deflate = -1 + * mod_headers = 0 + */ + ap_register_output_filter("MODSECURITY_OUT", output_filter, + NULL, AP_FTYPE_CONTENT_SET - 3); + + ap_register_output_filter("PDFP_OUT", pdfp_output_filter, + NULL, AP_FTYPE_CONTENT_SET); +} + +/* Defined in apache2_config.c */ +extern const command_rec module_directives[]; + +/* Module entry points */ +module AP_MODULE_DECLARE_DATA security2_module = { + STANDARD20_MODULE_STUFF, + create_directory_config, + merge_directory_configs, + NULL, /* create_server_config */ + NULL, /* merge_server_configs */ + module_directives, + register_hooks +}; diff --git a/2.5.13/2.5.x/apache2/mod_security2_config.h.in b/2.5.13/2.5.x/apache2/mod_security2_config.h.in new file mode 100644 index 00000000..95957bb5 --- /dev/null +++ b/2.5.13/2.5.x/apache2/mod_security2_config.h.in @@ -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 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 header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the 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 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 header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the 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 declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* Define for Solaris 2.5.1 so the uint8_t typedef from , + , or 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 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 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 diff --git a/2.5.13/2.5.x/apache2/mod_security2_config.hw b/2.5.13/2.5.x/apache2/mod_security2_config.hw new file mode 100644 index 00000000..c9500446 --- /dev/null +++ b/2.5.13/2.5.x/apache2/mod_security2_config.hw @@ -0,0 +1 @@ +/* This file is left empty for building on Windows. */ diff --git a/2.5.13/2.5.x/apache2/modsecurity.c b/2.5.13/2.5.x/apache2/modsecurity.c new file mode 100644 index 00000000..9785031b --- /dev/null +++ b/2.5.13/2.5.x/apache2/modsecurity.c @@ -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 + +#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=", 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; +} diff --git a/2.5.13/2.5.x/apache2/modsecurity.h b/2.5.13/2.5.x/apache2/modsecurity.h new file mode 100644 index 00000000..6141bd9b --- /dev/null +++ b/2.5.13/2.5.x/apache2/modsecurity.h @@ -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 +#include +#include + +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 +#else +#include +#include +#endif + +#define NOTE_MSR "modsecurity-tx-context" + +#define FATAL_ERROR "ModSecurity: Fatal error (memory allocation or unexpected internal error)!" + +extern DSOLOCAL char *new_server_signature; +extern DSOLOCAL char *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 diff --git a/2.5.13/2.5.x/apache2/modules.mk b/2.5.13/2.5.x/apache2/modules.mk new file mode 100644 index 00000000..d201d566 --- /dev/null +++ b/2.5.13/2.5.x/apache2/modules.mk @@ -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 diff --git a/2.5.13/2.5.x/apache2/msc_geo.c b/2.5.13/2.5.x/apache2/msc_geo.c new file mode 100644 index 00000000..061340db --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_geo.c @@ -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; +} + + diff --git a/2.5.13/2.5.x/apache2/msc_geo.h b/2.5.13/2.5.x/apache2/msc_geo.h new file mode 100644 index 00000000..e97d5a1a --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_geo.h @@ -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 +#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 diff --git a/2.5.13/2.5.x/apache2/msc_logging.c b/2.5.13/2.5.x/apache2/msc_logging.c new file mode 100644 index 00000000..c2336aff --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_logging.c @@ -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 + +#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); + } +} diff --git a/2.5.13/2.5.x/apache2/msc_logging.h b/2.5.13/2.5.x/apache2/msc_logging.h new file mode 100644 index 00000000..f0abb091 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_logging.h @@ -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 diff --git a/2.5.13/2.5.x/apache2/msc_lua.c b/2.5.13/2.5.x/apache2/msc_lua.c new file mode 100644 index 00000000..7d481471 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_lua.c @@ -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 */ diff --git a/2.5.13/2.5.x/apache2/msc_lua.h b/2.5.13/2.5.x/apache2/msc_lua.h new file mode 100644 index 00000000..cd60e17a --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_lua.h @@ -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 +#include +#include + +#include "apr_general.h" +#include "apr_tables.h" +#include "modsecurity.h" + +struct msc_script { + const char *name; + apr_array_header_t *parts; +}; + +struct msc_script_part { + const void *data; + size_t len; +}; + +char DSOLOCAL *lua_compile(msc_script **script, const char *filename, apr_pool_t *pool); + +int DSOLOCAL lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rule, char **error_msg); + +#endif + +#endif /* WITH_LUA */ diff --git a/2.5.13/2.5.x/apache2/msc_multipart.c b/2.5.13/2.5.x/apache2/msc_multipart.c new file mode 100644 index 00000000..9aaedd67 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_multipart.c @@ -0,0 +1,1396 @@ +/* + * 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 +#include + +#include "mod_security2_config.h" +#include "msc_multipart.h" +#include "msc_util.h" +#include "msc_parsers.h" + + +#if 0 +static char *multipart_construct_filename(modsec_rec *msr) { + char c, *p, *q = msr->mpd->mpp->filename; + char *filename; + + /* find the last backward slash and consider the + * filename to be only what's right from it + */ + p = strrchr(q, '\\'); + if (p != NULL) q = p + 1; + + /* do the same for the forward slash */ + p = strrchr(q, '/'); + if (p != NULL) q = p + 1; + + /* allow letters, digits and dots, replace + * everything else with underscores + */ + p = filename = apr_pstrdup(msr->mp, q); + while((c = *p) != 0) { + if (!( isalnum(c) || (c == '.') )) *p = '_'; + p++; + } + + return filename; +} +#endif + +/** + * + */ +static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value) { + char *p = NULL, *t = NULL; + + /* accept only what we understand */ + if (strncmp(c_d_value, "form-data", 9) != 0) { + return -1; + } + + /* see if there are any other parts to parse */ + + p = c_d_value + 9; + while((*p == '\t') || (*p == ' ')) p++; + if (*p == '\0') return 1; /* this is OK */ + + if (*p != ';') return -2; + p++; + + /* parse the appended parts */ + + while(*p != '\0') { + char *name = NULL, *value = NULL, *start = NULL; + + /* go over the whitespace */ + while((*p == '\t') || (*p == ' ')) p++; + if (*p == '\0') return -3; + + start = p; + while((*p != '\0') && (*p != '=') && (*p != '\t') && (*p != ' ')) p++; + if (*p == '\0') return -4; + + name = apr_pstrmemdup(msr->mp, start, (p - start)); + + while((*p == '\t') || (*p == ' ')) p++; + if (*p == '\0') return -5; + + if (*p != '=') return -13; + p++; + + while((*p == '\t') || (*p == ' ')) p++; + if (*p == '\0') return -6; + + /* Accept both quotes as some backends will accept them, but + * technically "'" is invalid and so flag_invalid_quoting is + * set so the user can deal with it in the rules if they so wish. + */ + if ((*p == '"') || (*p == '\'')) { + /* quoted */ + char quote = *p; + + if (quote == '\'') { + msr->mpd->flag_invalid_quoting = 1; + } + + p++; + if (*p == '\0') return -7; + + start = p; + value = apr_pstrdup(msr->mp, p); + t = value; + + while(*p != '\0') { + if (*p == '\\') { + if (*(p + 1) == '\0') { + /* improper escaping */ + return -8; + } + /* only quote and \ can be escaped */ + if ((*(p + 1) == quote) || (*(p + 1) == '\\')) { + p++; + } + else { + /* improper escaping */ + + /* We allow for now because IE sends + * improperly escaped content and there's + * nothing we can do about it. + * + * return -9; + */ + } + } + else if (*p == quote) { + *t = '\0'; + break; + } + + *(t++) = *(p++); + } + if (*p == '\0') return -10; + + p++; /* go over the quote at the end */ + + } else { + /* not quoted */ + + start = p; + while((*p != '\0') && (is_token_char(*p))) p++; + value = apr_pstrmemdup(msr->mp, start, (p - start)); + } + + /* evaluate part */ + + if (strcmp(name, "name") == 0) { + if (msr->mpd->mpp->name != NULL) { + msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition name: %s", + log_escape_nq(msr->mp, value)); + return -14; + } + msr->mpd->mpp->name = value; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Content-Disposition name: %s", + log_escape_nq(msr->mp, value)); + } + } + else + if (strcmp(name, "filename") == 0) { + if (msr->mpd->mpp->filename != NULL) { + msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition filename: %s", + log_escape_nq(msr->mp, value)); + return -15; + } + msr->mpd->mpp->filename = value; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Content-Disposition filename: %s", + log_escape_nq(msr->mp, value)); + } + } + else return -11; + + if (*p != '\0') { + while((*p == '\t') || (*p == ' ')) p++; + /* the next character must be a zero or a semi-colon */ + if (*p == '\0') return 1; /* this is OK */ + if (*p != ';') return -12; + p++; /* move over the semi-colon */ + } + + /* loop will stop when (*p == '\0') */ + } + + return 1; +} + +/** + * + */ +static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { + int i, len, rc; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + /* Check for nul bytes. */ + len = MULTIPART_BUF_SIZE - msr->mpd->bufleft; + for(i = 0; i < len; i++) { + if (msr->mpd->buf[i] == '\0') { + *error_msg = apr_psprintf(msr->mp, "Multipart: Nul byte in part headers."); + return -1; + } + } + + /* The buffer is data so increase the data length counter. */ + msr->msc_reqbody_no_files_length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft); + + if (len > 1) { + if (msr->mpd->buf[len - 2] == '\r') { + msr->mpd->flag_crlf_line = 1; + } else { + msr->mpd->flag_lf_line = 1; + } + } else { + msr->mpd->flag_lf_line = 1; + } + + /* Is this an empty line? */ + if ( ((msr->mpd->buf[0] == '\r') + &&(msr->mpd->buf[1] == '\n') + &&(msr->mpd->buf[2] == '\0') ) + || ((msr->mpd->buf[0] == '\n') + &&(msr->mpd->buf[1] == '\0') ) ) + { /* Empty line. */ + char *header_value = NULL; + + header_value = (char *)apr_table_get(msr->mpd->mpp->headers, "Content-Disposition"); + if (header_value == NULL) { + *error_msg = apr_psprintf(msr->mp, "Multipart: Part missing Content-Disposition header."); + return -1; + } + + rc = multipart_parse_content_disposition(msr, header_value); + if (rc < 0) { + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (%d): %s.", + rc, log_escape_nq(msr->mp, header_value)); + return -1; + } + + if (msr->mpd->mpp->name == NULL) { + *error_msg = apr_psprintf(msr->mp, "Multipart: Content-Disposition header missing name field."); + return -1; + } + + if (msr->mpd->mpp->filename != NULL) { + /* Some parsers use crude methods to extract the name and filename + * values from the C-D header. We need to check for the case where they + * didn't understand C-D but we did. + */ + if (strstr(header_value, "filename=") == NULL) { + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (filename)."); + return -1; + } + + msr->mpd->mpp->type = MULTIPART_FILE; + } else { + msr->mpd->mpp->type = MULTIPART_FORMDATA; + } + + msr->mpd->mpp_state = 1; + msr->mpd->mpp->last_header_name = NULL; + } else { + /* Header line. */ + + if (isspace(msr->mpd->buf[0])) { + char *header_value, *new_value, *data; + + /* header folding, add data to the header we are building */ + msr->mpd->flag_header_folding = 1; + + /* RFC-2557 states header folding is SP / HTAB, but PHP and + * perhaps others will take any whitespace. So, we accept, + * but with a flag set. + */ + if ((msr->mpd->buf[0] != '\t') && (msr->mpd->buf[0] != ' ')) { + msr->mpd->flag_invalid_header_folding = 1; + } + + if (msr->mpd->mpp->last_header_name == NULL) { + /* we are not building a header at this moment */ + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (folding error)."); + return -1; + } + + /* locate the beginning of data */ + data = msr->mpd->buf; + while(isspace(*data)) { + /* Flag invalid header folding if an invalid RFC-2557 character is used anywhere + * in the folding prefix. + */ + if ((*data != '\t') && (*data != ' ')) { + msr->mpd->flag_invalid_header_folding = 1; + } + data++; + } + + new_value = apr_pstrdup(msr->mp, data); + remove_lf_crlf_inplace(new_value); + + /* update the header value in the table */ + header_value = (char *)apr_table_get(msr->mpd->mpp->headers, msr->mpd->mpp->last_header_name); + new_value = apr_pstrcat(msr->mp, header_value, " ", new_value, NULL); + apr_table_set(msr->mpd->mpp->headers, msr->mpd->mpp->last_header_name, new_value); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Continued folder header \"%s\" with \"%s\"", + log_escape(msr->mp, msr->mpd->mpp->last_header_name), + log_escape(msr->mp, data)); + } + + if (strlen(new_value) > MULTIPART_BUF_SIZE) { + *error_msg = apr_psprintf(msr->mp, "Multipart: Part header too long."); + return -1; + } + } else { + char *header_name, *header_value, *data; + + /* new header */ + + data = msr->mpd->buf; + while((*data != ':') && (*data != '\0')) data++; + if (*data == '\0') { + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (colon missing): %s.", + log_escape_nq(msr->mp, msr->mpd->buf)); + return -1; + } + + /* extract header name */ + header_name = apr_pstrmemdup(msr->mp, msr->mpd->buf, (data - msr->mpd->buf)); + if (data == msr->mpd->buf) { + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (header name missing)."); + + return -1; + } + + /* extract the value value */ + data++; + while((*data == '\t') || (*data == ' ')) data++; + header_value = apr_pstrdup(msr->mp, data); + remove_lf_crlf_inplace(header_value); + + /* error if the name already exists */ + if (apr_table_get(msr->mpd->mpp->headers, header_name) != NULL) { + *error_msg = apr_psprintf(msr->mp, "Multipart: Duplicate part header: %s.", + log_escape_nq(msr->mp, header_name)); + return -1; + } + + apr_table_setn(msr->mpd->mpp->headers, header_name, header_value); + msr->mpd->mpp->last_header_name = header_name; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Added part header \"%s\" \"%s\"", + log_escape(msr->mp, header_name), + log_escape(msr->mp, header_value)); + } + } + } + + return 1; +} + +/** + * + */ +static int multipart_process_part_data(modsec_rec *msr, char **error_msg) { + char *p = msr->mpd->buf + (MULTIPART_BUF_SIZE - msr->mpd->bufleft); + char localreserve[2] = { '\0', '\0' }; /* initialized to quiet warning */ + int bytes_reserved = 0; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + /* Preserve some bytes for later. */ + if ( ((MULTIPART_BUF_SIZE - msr->mpd->bufleft) >= 1) + && (*(p - 1) == '\n') ) + { + if ( ((MULTIPART_BUF_SIZE - msr->mpd->bufleft) >= 2) + && (*(p - 2) == '\r') ) + { + /* Two bytes. */ + bytes_reserved = 2; + localreserve[0] = *(p - 2); + localreserve[1] = *(p - 1); + msr->mpd->bufleft += 2; + *(p - 2) = 0; + } else { + /* Only one byte. */ + bytes_reserved = 1; + localreserve[0] = *(p - 1); + localreserve[1] = 0; + msr->mpd->bufleft += 1; + *(p - 1) = 0; + } + } + + /* add data to the part we are building */ + if (msr->mpd->mpp->type == MULTIPART_FILE) { + int extract = msr->upload_extract_files; + + /* remember where we started */ + if (msr->mpd->mpp->length == 0) { + msr->mpd->mpp->offset = msr->mpd->buf_offset; + } + + /* check if the file limit has been reached */ + if (extract && (msr->mpd->nfiles >= msr->txcfg->upload_file_limit)) { + if (msr->mpd->flag_file_limit_exceeded == 0) { + *error_msg = apr_psprintf(msr->mp, + "Multipart: Upload file limit exceeded " + "SecUploadFileLimit %d.", + msr->txcfg->upload_file_limit); + msr_log(msr, 3, "%s", *error_msg); + + msr->mpd->flag_file_limit_exceeded = 1; + } + + extract = 0; + } + + /* only store individual files on disk if we are going + * to keep them or if we need to have them approved later + */ + if (extract) { + /* first create a temporary file if we don't have it already */ + if (msr->mpd->mpp->tmp_file_fd == 0) { + /* construct temporary file name */ + msr->mpd->mpp->tmp_file_name = apr_psprintf(msr->mp, "%s/%s-%s-file-XXXXXX", + msr->txcfg->tmp_dir, current_filetime(msr->mp), msr->txid); + msr->mpd->mpp->tmp_file_fd = msc_mkstemp_ex(msr->mpd->mpp->tmp_file_name, msr->txcfg->upload_filemode); + + /* do we have an opened file? */ + if (msr->mpd->mpp->tmp_file_fd < 0) { + *error_msg = apr_psprintf(msr->mp, "Multipart: Failed to create file: %s", + log_escape_nq(msr->mp, msr->mpd->mpp->tmp_file_name)); + return -1; + } + + /* keep track of the files count */ + msr->mpd->nfiles++; + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, + "Multipart: Created temporary file %d (mode %04o): %s", + msr->mpd->nfiles, + (unsigned int)msr->txcfg->upload_filemode, + log_escape_nq(msr->mp, msr->mpd->mpp->tmp_file_name)); + } + } + + /* write the reserve first */ + if (msr->mpd->reserve[0] != 0) { + if (write(msr->mpd->mpp->tmp_file_fd, &msr->mpd->reserve[1], msr->mpd->reserve[0]) != msr->mpd->reserve[0]) { + *error_msg = apr_psprintf(msr->mp, "Multipart: writing to \"%s\" failed", + log_escape(msr->mp, msr->mpd->mpp->tmp_file_name)); + return -1; + } + + msr->mpd->mpp->tmp_file_size += msr->mpd->reserve[0]; + msr->mpd->mpp->length += msr->mpd->reserve[0]; + } + + /* write data to the file */ + if (write(msr->mpd->mpp->tmp_file_fd, msr->mpd->buf, MULTIPART_BUF_SIZE - msr->mpd->bufleft) + != (MULTIPART_BUF_SIZE - msr->mpd->bufleft)) + { + *error_msg = apr_psprintf(msr->mp, "Multipart: writing to \"%s\" failed", + log_escape(msr->mp, msr->mpd->mpp->tmp_file_name)); + return -1; + } + + msr->mpd->mpp->tmp_file_size += (MULTIPART_BUF_SIZE - msr->mpd->bufleft); + msr->mpd->mpp->length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft); + } else { + /* just keep track of the file size */ + msr->mpd->mpp->tmp_file_size += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]; + msr->mpd->mpp->length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]; + } + } + else if (msr->mpd->mpp->type == MULTIPART_FORMDATA) { + value_part_t *value_part = apr_pcalloc(msr->mp, sizeof(value_part_t)); + + /* The buffer contains data so increase the data length counter. */ + msr->msc_reqbody_no_files_length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]; + + /* add this part to the list of parts */ + + /* remember where we started */ + if (msr->mpd->mpp->length == 0) { + msr->mpd->mpp->offset = msr->mpd->buf_offset; + } + + if (msr->mpd->reserve[0] != 0) { + value_part->data = apr_palloc(msr->mp, (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]); + memcpy(value_part->data, &(msr->mpd->reserve[1]), msr->mpd->reserve[0]); + memcpy(value_part->data + msr->mpd->reserve[0], msr->mpd->buf, (MULTIPART_BUF_SIZE - msr->mpd->bufleft)); + + value_part->length = (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]; + msr->mpd->mpp->length += value_part->length; + } else { + value_part->length = (MULTIPART_BUF_SIZE - msr->mpd->bufleft); + value_part->data = apr_pstrmemdup(msr->mp, msr->mpd->buf, value_part->length); + msr->mpd->mpp->length += value_part->length; + } + + *(value_part_t **)apr_array_push(msr->mpd->mpp->value_parts) = value_part; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Added data to variable: %s", + log_escape_nq_ex(msr->mp, value_part->data, value_part->length)); + } + } + else { + *error_msg = apr_psprintf(msr->mp, "Multipart: unknown part type %d", msr->mpd->mpp->type); + return -1; + } + + /* store the reserved bytes to the multipart + * context so that they don't get lost + */ + if (bytes_reserved) { + msr->mpd->reserve[0] = bytes_reserved; + msr->mpd->reserve[1] = localreserve[0]; + msr->mpd->reserve[2] = localreserve[1]; + msr->mpd->buf_offset += bytes_reserved; + } + else { + msr->mpd->buf_offset -= msr->mpd->reserve[0]; + msr->mpd->reserve[0] = 0; + } + + return 1; +} + +/** + * + */ +static char *multipart_combine_value_parts(modsec_rec *msr, apr_array_header_t *value_parts) { + value_part_t **parts = NULL; + char *rval = apr_palloc(msr->mp, msr->mpd->mpp->length + 1); + unsigned long int offset; + int i; + + if (rval == NULL) return NULL; + + offset = 0; + parts = (value_part_t **)value_parts->elts; + for(i = 0; i < value_parts->nelts; i++) { + if (offset + parts[i]->length <= msr->mpd->mpp->length) { + memcpy(rval + offset, parts[i]->data, parts[i]->length); + offset += parts[i]->length; + } + } + rval[offset] = '\0'; + + return rval; +} + +/** + * + */ +static int multipart_process_boundary(modsec_rec *msr, int last_part, char **error_log) { + /* if there was a part being built finish it */ + if (msr->mpd->mpp != NULL) { + /* close the temp file */ + if ((msr->mpd->mpp->type == MULTIPART_FILE) + &&(msr->mpd->mpp->tmp_file_name != NULL) + &&(msr->mpd->mpp->tmp_file_fd != 0)) + { + close(msr->mpd->mpp->tmp_file_fd); + } + + if (msr->mpd->mpp->type != MULTIPART_FILE) { + /* now construct a single string out of the parts */ + msr->mpd->mpp->value = multipart_combine_value_parts(msr, msr->mpd->mpp->value_parts); + if (msr->mpd->mpp->value == NULL) return -1; + } + + if (msr->mpd->mpp->name) { + /* add the part to the list of parts */ + *(multipart_part **)apr_array_push(msr->mpd->parts) = msr->mpd->mpp; + if (msr->mpd->mpp->type == MULTIPART_FILE) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Added file part %pp to the list: name \"%s\" " + "file name \"%s\" (offset %u, length %u)", + msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name), + log_escape(msr->mp, msr->mpd->mpp->filename), + msr->mpd->mpp->offset, msr->mpd->mpp->length); + } + } + else { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Added part %pp to the list: name \"%s\" " + "(offset %u, length %u)", msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name), + msr->mpd->mpp->offset, msr->mpd->mpp->length); + } + } + } + else { + msr_log(msr, 3, "Multipart: Skipping invalid part %pp (part name missing): " + "(offset %u, length %u)", msr->mpd->mpp, + msr->mpd->mpp->offset, msr->mpd->mpp->length); + } + + msr->mpd->mpp = NULL; + } + + if (last_part == 0) { + /* start building a new part */ + msr->mpd->mpp = (multipart_part *)apr_pcalloc(msr->mp, sizeof(multipart_part)); + if (msr->mpd->mpp == NULL) return -1; + msr->mpd->mpp->type = MULTIPART_FORMDATA; + msr->mpd->mpp_state = 0; + + msr->mpd->mpp->headers = apr_table_make(msr->mp, 10); + if (msr->mpd->mpp->headers == NULL) return -1; + msr->mpd->mpp->last_header_name = NULL; + + msr->mpd->reserve[0] = 0; + msr->mpd->reserve[1] = 0; + msr->mpd->reserve[2] = 0; + msr->mpd->reserve[3] = 0; + + msr->mpd->mpp->value_parts = apr_array_make(msr->mp, 10, sizeof(value_part_t *)); + } + + return 1; +} + +static int multipart_boundary_characters_valid(char *boundary) { + unsigned char *p = (unsigned char *)boundary; + unsigned char c; + + if (p == NULL) return -1; + + while((c = *p) != '\0') { + /* Control characters and space not allowed. */ + if (c < 32) { + return 0; + } + + /* Non-ASCII characters not allowed. */ + if (c > 126) { + return 0; + } + + switch(c) { + /* Special characters not allowed. */ + case '(' : + case ')' : + case '<' : + case '>' : + case '@' : + case ',' : + case ';' : + case ':' : + case '\\' : + case '"' : + case '/' : + case '[' : + case ']' : + case '?' : + case '=' : + return 0; + break; + + default : + /* Do nothing. */ + break; + } + + p++; + } + + return 1; +} + +static int multipart_count_boundary_params(apr_pool_t *mp, const char *header_value) { + char *duplicate = NULL; + char *s = NULL; + int count = 0; + + if (header_value == NULL) return -1; + duplicate = apr_pstrdup(mp, header_value); + if (duplicate == NULL) return -1; + + /* Performing a case-insensitive search. */ + strtolower_inplace((unsigned char *)duplicate); + + s = duplicate; + while((s = strstr(s, "boundary")) != NULL) { + s += 8; + + if (strchr(s, '=') != NULL) { + count++; + } + } + + return count; +} + +/** + * + */ +int multipart_init(modsec_rec *msr, char **error_msg) { + if (error_msg == NULL) return -1; + *error_msg = NULL; + + msr->mpd = (multipart_data *)apr_pcalloc(msr->mp, sizeof(multipart_data)); + if (msr->mpd == NULL) return -1; + + msr->mpd->parts = apr_array_make(msr->mp, 10, sizeof(multipart_part *)); + msr->mpd->bufleft = MULTIPART_BUF_SIZE; + msr->mpd->bufptr = msr->mpd->buf; + msr->mpd->buf_contains_line = 1; + msr->mpd->mpp = NULL; + + if (msr->request_content_type == NULL) { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Content-Type header not available."); + return -1; + } + + if (strlen(msr->request_content_type) > 1024) { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (length)."); + return -1; + } + + if (strncasecmp(msr->request_content_type, "multipart/form-data", 19) != 0) { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid MIME type."); + return -1; + } + + /* Count how many times the word "boundary" appears in the C-T header. */ + if (multipart_count_boundary_params(msr->mp, msr->request_content_type) > 1) { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Multiple boundary parameters in C-T."); + return -1; + } + + msr->mpd->boundary = strstr(msr->request_content_type, "boundary"); + if (msr->mpd->boundary != NULL) { + char *p = NULL; + char *b = NULL; + int seen_semicolon = 0; + int len = 0; + + /* Check for extra characters before the boundary. */ + for (p = (char *)(msr->request_content_type + 19); p < msr->mpd->boundary; p++) { + if (!isspace(*p)) { + if ((seen_semicolon == 0) && (*p == ';')) { + seen_semicolon = 1; /* It is OK to have one semicolon. */ + } else { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (malformed)."); + return -1; + } + } + } + + /* Have we seen the semicolon in the header? */ + if (seen_semicolon == 0) { + msr->mpd->flag_missing_semicolon = 1; + } + + b = strchr(msr->mpd->boundary + 8, '='); + if (b == NULL) { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (malformed)."); + return -1; + } + + /* Check parameter name ends well. */ + if (b != (msr->mpd->boundary + 8)) { + /* Check all characters between the end of the boundary + * and the = character. + */ + for (p = msr->mpd->boundary + 8; p < b; p++) { + if (isspace(*p)) { + /* Flag for whitespace after parameter name. */ + msr->mpd->flag_boundary_whitespace = 1; + } else { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (parameter name)."); + return -1; + } + } + } + + b++; /* Go over the = character. */ + len = strlen(b); + + /* Flag for whitespace before parameter value. */ + if (isspace(*b)) { + msr->mpd->flag_boundary_whitespace = 1; + } + + /* Is the boundary quoted? */ + if ((len >= 2) && (*b == '"') && (*(b + len - 1) == '"')) { + /* Quoted. */ + msr->mpd->boundary = apr_pstrndup(msr->mp, b + 1, len - 2); + if (msr->mpd->boundary == NULL) return -1; + msr->mpd->flag_boundary_quoted = 1; + } else { + /* Not quoted. */ + + /* Test for partial quoting. */ + if ( (*b == '"') + || ((len >= 2) && (*(b + len - 1) == '"')) ) + { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (quote)."); + return -1; + } + + msr->mpd->boundary = apr_pstrdup(msr->mp, b); + if (msr->mpd->boundary == NULL) return -1; + msr->mpd->flag_boundary_quoted = 0; + } + + /* Case-insensitive test for the string "boundary" in the boundary. */ + if (multipart_count_boundary_params(msr->mp, msr->mpd->boundary) != 0) { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (content)."); + return -1; + } + + /* Validate the characters used in the boundary. */ + if (multipart_boundary_characters_valid(msr->mpd->boundary) != 1) { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (characters)."); + return -1; + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Boundary%s: %s", + (msr->mpd->flag_boundary_quoted ? " (quoted)" : ""), + log_escape_nq(msr->mp, msr->mpd->boundary)); + } + + if (strlen(msr->mpd->boundary) == 0) { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (empty)."); + return -1; + } + } + else { /* Could not find boundary in the C-T header. */ + msr->mpd->flag_error = 1; + + /* Test for case-insensitive boundary. Allowed by the RFC but highly unusual. */ + if (multipart_count_boundary_params(msr->mp, msr->request_content_type) > 0) { + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (case sensitivity)."); + return -1; + } + + *error_msg = apr_psprintf(msr->mp, "Multipart: Boundary not found in C-T."); + return -1; + } + + return 1; +} + +/** + * Finalise multipart processing. This method is invoked at the end, when it + * is clear that there is no more data to be processed. + */ +int multipart_complete(modsec_rec *msr, char **error_msg) { + if (msr->mpd == NULL) return 1; + + if (msr->txcfg->debuglog_level >= 4) { + if (msr->mpd->flag_data_before) { + msr_log(msr, 4, "Multipart: Warning: seen data before first boundary."); + } + + if (msr->mpd->flag_data_after) { + msr_log(msr, 4, "Multipart: Warning: seen data after last boundary."); + } + + if (msr->mpd->flag_boundary_quoted) { + msr_log(msr, 4, "Multipart: Warning: boundary was quoted."); + } + + if (msr->mpd->flag_boundary_whitespace) { + msr_log(msr, 4, "Multipart: Warning: boundary whitespace in C-T header."); + } + + if (msr->mpd->flag_header_folding) { + msr_log(msr, 4, "Multipart: Warning: header folding used."); + } + + if (msr->mpd->flag_crlf_line && msr->mpd->flag_lf_line) { + msr_log(msr, 4, "Multipart: Warning: mixed line endings used (CRLF/LF)."); + } + else if (msr->mpd->flag_lf_line) { + msr_log(msr, 4, "Multipart: Warning: incorrect line endings used (LF)."); + } + + if (msr->mpd->flag_missing_semicolon) { + msr_log(msr, 4, "Multipart: Warning: missing semicolon in C-T header."); + } + + if (msr->mpd->flag_invalid_quoting) { + msr_log(msr, 4, "Multipart: Warning: invalid quoting used."); + } + + if (msr->mpd->flag_invalid_header_folding) { + msr_log(msr, 4, "Multipart: Warning: invalid header folding used."); + } + } + + if ((msr->mpd->seen_data != 0) && (msr->mpd->is_complete == 0)) { + if (msr->mpd->boundary_count > 0) { + /* Check if we have the final boundary (that we haven't + * processed yet) in the buffer. + */ + if (msr->mpd->buf_contains_line) { + if ( ((unsigned int)(MULTIPART_BUF_SIZE - msr->mpd->bufleft) == (4 + strlen(msr->mpd->boundary))) + && (*(msr->mpd->buf) == '-') + && (*(msr->mpd->buf + 1) == '-') + && (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) + && (*(msr->mpd->buf + 2 + strlen(msr->mpd->boundary)) == '-') + && (*(msr->mpd->buf + 2 + strlen(msr->mpd->boundary) + 1) == '-') ) + { + /* Looks like the final boundary - process it. */ + if (multipart_process_boundary(msr, 1 /* final */, error_msg) < 0) { + msr->mpd->flag_error = 1; + return -1; + } + + /* The payload is complete after all. */ + msr->mpd->is_complete = 1; + } + } + + if (msr->mpd->is_complete == 0) { + *error_msg = apr_psprintf(msr->mp, "Multipart: Final boundary missing."); + return -1; + } + } else { + *error_msg = apr_psprintf(msr->mp, "Multipart: No boundaries found in payload."); + return -1; + } + } + + return 1; +} + +/** + * + */ +int multipart_process_chunk(modsec_rec *msr, const char *buf, + unsigned int size, char **error_msg) +{ + char *inptr = (char *)buf; + unsigned int inleft = size; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (size == 0) return 1; + + msr->mpd->seen_data = 1; + + if (msr->mpd->is_complete) { + msr->mpd->flag_data_before = 1; + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Ignoring data after last boundary (received %u bytes)", size); + } + + return 1; + } + + if (msr->mpd->bufleft == 0) { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, + "Multipart: Internal error in process_chunk: no space left in the buffer"); + return -1; + } + + /* here we loop through the available data, one byte at a time */ + while(inleft > 0) { + char c = *inptr; + int process_buffer = 0; + + if ((c == '\r') && (msr->mpd->bufleft == 1)) { + /* we don't want to take \r as the last byte in the buffer */ + process_buffer = 1; + } else { + inptr++; + inleft = inleft - 1; + + *(msr->mpd->bufptr) = c; + msr->mpd->bufptr++; + msr->mpd->bufleft--; + } + + /* until we either reach the end of the line + * or the end of our internal buffer + */ + if ((c == '\n') || (msr->mpd->bufleft == 0) || (process_buffer)) { + int processed_as_boundary = 0; + + *(msr->mpd->bufptr) = 0; + + /* Do we have something that looks like a boundary? */ + if ( msr->mpd->buf_contains_line + && (strlen(msr->mpd->buf) > 3) + && (*(msr->mpd->buf) == '-') + && (*(msr->mpd->buf + 1) == '-') ) + { + /* Does it match our boundary? */ + if ( (strlen(msr->mpd->buf) >= strlen(msr->mpd->boundary) + 2) + && (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) ) + { + char *boundary_end = msr->mpd->buf + 2 + strlen(msr->mpd->boundary); + int is_final = 0; + + /* Is this the final boundary? */ + if ((*boundary_end == '-') && (*(boundary_end + 1)== '-')) { + is_final = 1; + boundary_end += 2; + + if (msr->mpd->is_complete != 0) { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, + "Multipart: Invalid boundary (final duplicate)."); + return -1; + } + } + + /* Allow for CRLF and LF line endings. */ + if ( ( (*boundary_end == '\r') + && (*(boundary_end + 1) == '\n') + && (*(boundary_end + 2) == '\0') ) + || ( (*boundary_end == '\n') + && (*(boundary_end + 1) == '\0') ) ) + { + if (*boundary_end == '\n') { + msr->mpd->flag_lf_line = 1; + } else { + msr->mpd->flag_crlf_line = 1; + } + + if (multipart_process_boundary(msr, (is_final ? 1 : 0), error_msg) < 0) { + msr->mpd->flag_error = 1; + return -1; + } + + if (is_final) { + msr->mpd->is_complete = 1; + } + + processed_as_boundary = 1; + msr->mpd->boundary_count++; + } + else { + /* error */ + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, + "Multipart: Invalid boundary: %s", + log_escape_nq(msr->mp, msr->mpd->buf)); + return -1; + } + } else { /* It looks like a boundary but we couldn't match it. */ + char *p = NULL; + + /* Check if an attempt to use quotes around the boundary was made. */ + if ( (msr->mpd->flag_boundary_quoted) + && (strlen(msr->mpd->buf) >= strlen(msr->mpd->boundary) + 3) + && (*(msr->mpd->buf + 2) == '"') + && (strncmp(msr->mpd->buf + 3, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) + ) { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary (quotes)."); + return -1; + } + + /* Check the beginning of the boundary for whitespace. */ + p = msr->mpd->buf + 2; + while(isspace(*p)) { + p++; + } + + if ( (p != msr->mpd->buf + 2) + && (strncmp(p, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) + ) { + /* Found whitespace in front of a boundary. */ + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary (whitespace)."); + return -1; + } + + msr->mpd->flag_unmatched_boundary = 1; + } + } else { /* We do not think the buffer contains a boundary. */ + /* Look into the buffer to see if there's anything + * there that resembles a boundary. + */ + if (msr->mpd->buf_contains_line) { + int i, len = (MULTIPART_BUF_SIZE - msr->mpd->bufleft); + char *p = msr->mpd->buf; + + for(i = 0; i < len; i++) { + if ((p[i] == '-') && (i + 1 < len) && (p[i + 1] == '-')) + { + if (strncmp(p + i + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) { + msr->mpd->flag_unmatched_boundary = 1; + break; + } + } + } + } + } + + /* Process as data if it was not a boundary. */ + if (processed_as_boundary == 0) { + if (msr->mpd->mpp == NULL) { + msr->mpd->flag_data_before = 1; + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Ignoring data before first boundary."); + } + } else { + if (msr->mpd->mpp_state == 0) { + if ((msr->mpd->bufleft == 0) || (process_buffer)) { + /* part header lines must be shorter than + * MULTIPART_BUF_SIZE bytes + */ + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, + "Multipart: Part header line over %d bytes long", + MULTIPART_BUF_SIZE); + return -1; + } + + if (multipart_process_part_header(msr, error_msg) < 0) { + msr->mpd->flag_error = 1; + return -1; + } + } else { + if (multipart_process_part_data(msr, error_msg) < 0) { + msr->mpd->flag_error = 1; + return -1; + } + } + } + } + + /* Update the offset of the data we are about + * to process. This is to allow us to know the + * offsets of individual files and variables. + */ + msr->mpd->buf_offset += (MULTIPART_BUF_SIZE - msr->mpd->bufleft); + + /* reset the pointer to the beginning of the buffer + * and continue to accept input data + */ + msr->mpd->bufptr = msr->mpd->buf; + msr->mpd->bufleft = MULTIPART_BUF_SIZE; + msr->mpd->buf_contains_line = (c == 0x0a) ? 1 : 0; + } + + if ((msr->mpd->is_complete) && (inleft != 0)) { + msr->mpd->flag_data_after = 1; + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Ignoring data after last boundary (%u bytes left)", inleft); + } + + return 1; + } + } + + return 1; +} + +/** + * + */ +apr_status_t multipart_cleanup(modsec_rec *msr) { + int keep_files = 0; + + if (msr->mpd == NULL) return -1; + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Cleanup started (remove files %d).", msr->upload_remove_files); + } + + if (msr->upload_remove_files == 0) { + if (msr->txcfg->upload_dir == NULL) { + msr_log(msr, 1, "Input filter: SecUploadDir is undefined, unable to store " + "multipart files."); + } else { + keep_files = 1; + } + } + + /* Loop through the list of parts + * and delete the temporary files, but only if + * file storage was not requested, or if storage + * of relevant files was requested and this isn't + * such a request. + */ + if (keep_files == 0) { + multipart_part **parts; + int i; + + parts = (multipart_part **)msr->mpd->parts->elts; + for(i = 0; i < msr->mpd->parts->nelts; i++) { + if (parts[i]->type == MULTIPART_FILE) { + if (parts[i]->tmp_file_name != NULL) { + /* make sure it is closed first */ + if (parts[i]->tmp_file_fd > 0) { + close(parts[i]->tmp_file_fd); + parts[i]->tmp_file_fd = -1; + } + + if (unlink(parts[i]->tmp_file_name) < 0) { + msr_log(msr, 1, "Multipart: Failed to delete file (part) \"%s\" because %d(%s)", + log_escape(msr->mp, parts[i]->tmp_file_name), errno, strerror(errno)); + } else { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Deleted file (part) \"%s\"", + log_escape(msr->mp, parts[i]->tmp_file_name)); + } + } + } + } + } + } else { + /* delete empty files, move the others to the upload dir */ + multipart_part **parts; + int i; + + parts = (multipart_part **)msr->mpd->parts->elts; + for(i = 0; i < msr->mpd->parts->nelts; i++) { + if ( (parts[i]->type == MULTIPART_FILE) + && (parts[i]->tmp_file_size == 0)) + { + /* Delete empty file. */ + if (parts[i]->tmp_file_name != NULL) { + /* make sure it is closed first */ + if (parts[i]->tmp_file_fd > 0) { + close(parts[i]->tmp_file_fd); + parts[i]->tmp_file_fd = -1; + } + + if (unlink(parts[i]->tmp_file_name) < 0) { + msr_log(msr, 1, "Multipart: Failed to delete empty file (part) \"%s\" because %d(%s)", + log_escape(msr->mp, parts[i]->tmp_file_name), errno, strerror(errno)); + } else { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Deleted empty file (part) \"%s\"", + log_escape(msr->mp, parts[i]->tmp_file_name)); + } + } + } + } else { + /* Move file to the upload dir. */ + if (parts[i]->tmp_file_name != NULL) { + const char *new_filename = NULL; + const char *new_basename = NULL; + + /* make sure it is closed first */ + if (parts[i]->tmp_file_fd > 0) { + close(parts[i]->tmp_file_fd); + parts[i]->tmp_file_fd = -1; + } + + new_basename = file_basename(msr->mp, parts[i]->tmp_file_name); + if (new_basename == NULL) return -1; + new_filename = apr_psprintf(msr->mp, "%s/%s", msr->txcfg->upload_dir, + new_basename); + if (new_filename == NULL) return -1; + + if (apr_file_rename(parts[i]->tmp_file_name, new_filename, + msr->msc_reqbody_mp) != APR_SUCCESS) + { + msr_log(msr, 1, "Input filter: Failed to rename file from \"%s\" to \"%s\".", + log_escape(msr->mp, parts[i]->tmp_file_name), + log_escape(msr->mp, new_filename)); + return -1; + } else { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Input filter: Moved file from \"%s\" to \"%s\".", + log_escape(msr->mp, parts[i]->tmp_file_name), + log_escape(msr->mp, new_filename)); + } + } + } + } + } + } + + return 1; +} + +/** + * + */ +int multipart_get_arguments(modsec_rec *msr, char *origin, apr_table_t *arguments) { + multipart_part **parts; + int i; + + parts = (multipart_part **)msr->mpd->parts->elts; + for(i = 0; i < msr->mpd->parts->nelts; i++) { + if (parts[i]->type == MULTIPART_FORMDATA) { + msc_arg *arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg)); + if (arg == NULL) return -1; + + arg->name = parts[i]->name; + arg->name_len = strlen(parts[i]->name); + arg->value = parts[i]->value; + arg->value_len = parts[i]->length; + arg->value_origin_offset = parts[i]->offset; + arg->value_origin_len = parts[i]->length; + arg->origin = origin; + + add_argument(msr, arguments, arg); + } + } + + return 1; +} + +/** + * + */ +char *multipart_reconstruct_urlencoded_body_sanitise(modsec_rec *msr) { + multipart_part **parts; + char *body; + unsigned int body_len; + int i; + + if (msr->mpd == NULL) return NULL; + + /* calculate the size of the buffer */ + body_len = 1; + parts = (multipart_part **)msr->mpd->parts->elts; + for(i = 0; i < msr->mpd->parts->nelts; i++) { + if (parts[i]->type == MULTIPART_FORMDATA) { + body_len += 4; + body_len += strlen(parts[i]->name) * 3; + body_len += strlen(parts[i]->value) * 3; + } + } + + /* allocate the buffer */ + body = apr_palloc(msr->mp, body_len + 1); + if ((body == NULL) || (body_len + 1 == 0)) return NULL; + *body = 0; + + parts = (multipart_part **)msr->mpd->parts->elts; + for(i = 0; i < msr->mpd->parts->nelts; i++) { + if (parts[i]->type == MULTIPART_FORMDATA) { + if (*body != 0) { + strncat(body, "&", body_len - strlen(body)); + } + strnurlencat(body, parts[i]->name, body_len - strlen(body)); + strncat(body, "=", body_len - strlen(body)); + + /* Sanitise the variable. Since we are only doing this for + * the logging we will actually write over the data we keep + * in the memory. + */ + if (msr->phase >= PHASE_LOGGING) { + if (apr_table_get(msr->arguments_to_sanitise, parts[i]->name) != NULL) { + memset(parts[i]->value, '*', strlen(parts[i]->value)); + } + } + strnurlencat(body, parts[i]->value, body_len - strlen(body)); + } + } + + return body; +} diff --git a/2.5.13/2.5.x/apache2/msc_multipart.h b/2.5.13/2.5.x/apache2/msc_multipart.h new file mode 100644 index 00000000..ed5b5834 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_multipart.h @@ -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 diff --git a/2.5.13/2.5.x/apache2/msc_parsers.c b/2.5.13/2.5.x/apache2/msc_parsers.c new file mode 100644 index 00000000..83736f62 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_parsers.c @@ -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 + +/** + * + */ +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); +} + diff --git a/2.5.13/2.5.x/apache2/msc_parsers.h b/2.5.13/2.5.x/apache2/msc_parsers.h new file mode 100644 index 00000000..30ff6bca --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_parsers.h @@ -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 diff --git a/2.5.13/2.5.x/apache2/msc_pcre.c b/2.5.13/2.5.x/apache2/msc_pcre.c new file mode 100644 index 00000000..e82ca1a4 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_pcre.c @@ -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); +} + diff --git a/2.5.13/2.5.x/apache2/msc_pcre.h b/2.5.13/2.5.x/apache2/msc_pcre.h new file mode 100644 index 00000000..a8c2e18a --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_pcre.h @@ -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 diff --git a/2.5.13/2.5.x/apache2/msc_release.c b/2.5.13/2.5.x/apache2/msc_release.c new file mode 100644 index 00000000..82667ce5 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_release.c @@ -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 */ +} diff --git a/2.5.13/2.5.x/apache2/msc_release.h b/2.5.13/2.5.x/apache2/msc_release.h new file mode 100644 index 00000000..c0566e79 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_release.h @@ -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 +#include + +/* ENH: Clean this mess up by detecting this is possible */ +#if !(defined(_AIX) || defined(WIN32) || defined(CYGWIN) || defined(NETWARE) || defined(SOLARIS2) || defined(OSF1)) +#define DSOLOCAL __attribute__((visibility("hidden"))) +#else +#define DSOLOCAL +#endif + +#if defined(DEBUG_MEM) +/* Nothing Yet */ +#endif + +/* For GNU C, tell the compiler to check printf like formatters */ +#if (defined(__GNUC__) && !defined(SOLARIS2)) +#define PRINTF_ATTRIBUTE(a,b) __attribute__((format (printf, a, b))) +#else +#define PRINTF_ATTRIBUTE(a,b) +#endif + +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_ */ diff --git a/2.5.13/2.5.x/apache2/msc_reqbody.c b/2.5.13/2.5.x/apache2/msc_reqbody.c new file mode 100644 index 00000000..18da2f9b --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_reqbody.c @@ -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; +} diff --git a/2.5.13/2.5.x/apache2/msc_test.c b/2.5.13/2.5.x/apache2/msc_test.c new file mode 100644 index 00000000..7cacccf7 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_test.c @@ -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 +#include + +#include "modsecurity.h" +#include "re.h" +#include "pdf_protect.h" + +#define ISHEX(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F'))) + +#define BUFLEN 32768 + +#define RESULT_SUCCESS 0 +#define RESULT_ERROR -1 +#define RESULT_MISMATCHED -2 +#define RESULT_WRONGSIZE -3 +#define RESULT_WRONGRET -4 + +#define DEFAULT_ACTION "phase:2,log,auditlog,pass" + +#define CMDLINE_OPTS "t:n:p:P:r:I:D:Nh" + +/* Types */ +typedef struct tfn_data_t tfn_data_t; +typedef struct op_data_t op_data_t; +typedef struct action_data_t action_data_t; + +struct tfn_data_t { + const char *name; + const char *param; + unsigned char *input; + apr_size_t input_len; + msre_tfn_metadata *metadata; +}; + +struct op_data_t { + const char *name; + const char *param; + unsigned char *input; + apr_size_t input_len; + msre_ruleset *ruleset; + msre_rule *rule; + msre_var *var; + msre_op_metadata *metadata; +}; + +struct action_data_t { + const char *name; + unsigned char *input; + apr_size_t input_len; + msre_ruleset *ruleset; + msre_rule *rule; + msre_var *var; + msre_actionset *actionset; + msre_action *action; +}; + + +/* Globals */ +static int debuglog_level = 0; +static char *test_name = NULL; +static apr_pool_t *g_mp = NULL; +static modsec_rec *g_msr = NULL; +static unsigned char buf[BUFLEN]; +msc_engine *modsecurity = NULL; +unsigned long int DSOLOCAL msc_pcre_match_limit = 0; +unsigned long int DSOLOCAL msc_pcre_match_limit_recursion = 0; + +/* Stubs */ +char *format_error_log_message(apr_pool_t *mp, error_message *em) { + return "FAKE ERROR LOG MESSAGE"; +} + +apr_status_t send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status) { + return APR_SUCCESS; +} + +int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char **output) { + return 0; +} + +char *get_apr_error(apr_pool_t *p, apr_status_t rc) { + char *text = apr_pcalloc(p, 201); + if (text == NULL) return NULL; + apr_strerror(rc, text, 200); + return text; +} + +void msr_log(modsec_rec *msr, int level, const char *text, ...) { + va_list ap; + char str1[1024] = ""; + char str2[1256] = ""; + + if ((msr == NULL) || (level > msr->txcfg->debuglog_level)) { + return; + } + if (msr->txcfg->debuglog_fd == NOT_SET_P) { + if (apr_file_open(&msr->txcfg->debuglog_fd, msr->txcfg->debuglog_name, APR_READ|APR_WRITE|APR_CREATE|APR_APPEND|APR_BINARY, APR_OS_DEFAULT, g_mp) != APR_SUCCESS) { + fprintf(stderr, "ERROR: failed to create unit test debug log \"%s\".\n", msr->txcfg->debuglog_name); + msr->txcfg->debuglog_fd = NULL; + } + } + + va_start(ap, text); + if (msr->txcfg->debuglog_fd != NULL) { + apr_size_t nbytes_written = 0; + apr_vsnprintf(str1, sizeof(str1), text, ap); + apr_snprintf(str2, sizeof(str2), "%lu: [%d] [%s] %s\n", (unsigned long)getpid(), level, test_name, str1); + + apr_file_write_full(msr->txcfg->debuglog_fd, str2, strlen(str2), &nbytes_written); + } + va_end(ap); +} + +void msr_log_error(modsec_rec *msr, const char *text, ...) { + va_list ap; + int level = 3; + char str1[1024] = ""; + char str2[1256] = ""; + + if ((msr == NULL) || (level > msr->txcfg->debuglog_level)) { + return; + } + if (msr->txcfg->debuglog_fd == NOT_SET_P) { + if (apr_file_open(&msr->txcfg->debuglog_fd, msr->txcfg->debuglog_name, APR_READ|APR_WRITE|APR_CREATE|APR_APPEND|APR_BINARY, APR_OS_DEFAULT, g_mp) != APR_SUCCESS) { + fprintf(stderr, "ERROR: failed to create unit test debug log \"%s\".\n", msr->txcfg->debuglog_name); + msr->txcfg->debuglog_fd = NULL; + } + } + + va_start(ap, text); + if (msr->txcfg->debuglog_fd != NULL) { + apr_size_t nbytes_written = 0; + apr_vsnprintf(str1, sizeof(str1), text, ap); + apr_snprintf(str2, sizeof(str2), "%lu: [%d] [%s] %s\n", (unsigned long)getpid(), level, test_name, str1); + + apr_file_write_full(msr->txcfg->debuglog_fd, str2, strlen(str2), &nbytes_written); + } + va_end(ap); +} + +void msr_log_warn(modsec_rec *msr, const char *text, ...) { + va_list ap; + int level = 4; + char str1[1024] = ""; + char str2[1256] = ""; + + if ((msr == NULL) || (level > msr->txcfg->debuglog_level)) { + return; + } + if (msr->txcfg->debuglog_fd == NOT_SET_P) { + if (apr_file_open(&msr->txcfg->debuglog_fd, msr->txcfg->debuglog_name, APR_READ|APR_WRITE|APR_CREATE|APR_APPEND|APR_BINARY, APR_OS_DEFAULT, g_mp) != APR_SUCCESS) { + fprintf(stderr, "ERROR: failed to create unit test debug log \"%s\".\n", msr->txcfg->debuglog_name); + msr->txcfg->debuglog_fd = NULL; + } + } + + va_start(ap, text); + if (msr->txcfg->debuglog_fd != NULL) { + apr_size_t nbytes_written = 0; + apr_vsnprintf(str1, sizeof(str1), text, ap); + apr_snprintf(str2, sizeof(str2), "%lu: [%d] [%s] %s\n", (unsigned long)getpid(), level, test_name, str1); + + apr_file_write_full(msr->txcfg->debuglog_fd, str2, strlen(str2), &nbytes_written); + } + va_end(ap); +} + +const char *ap_get_remote_host(conn_rec *conn, void *dir_config, int type, int *str_is_ip) { + return "FAKE-REMOTE-HOST"; +} + +char *get_env_var(request_rec *r, char *name) { + return "FAKE-ENV-VAR"; +} + +apr_status_t unixd_set_global_mutex_perms(apr_global_mutex_t *gmutex) { + return APR_SUCCESS; +} + +apr_status_t unixd_set_proc_mutex_perms(apr_proc_mutex_t *pmutex) { + return APR_SUCCESS; +} + + +/* Escaping functions */ + +static unsigned char hex2dec(unsigned char *what) { + register unsigned char digit; + + digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); + digit *= 16; + digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0')); + + return digit; +} + +static unsigned char *unescape_inplace(unsigned char *str, apr_size_t *len) +{ + apr_size_t i, j; + for (i = j = 0; i < *len; j++) { + if ((str[i] == '\\') && (i + 3 < *len) && (str[i + 1] == 'x') && ISHEX(str[i + 2]) && ISHEX(str[i + 3]) ) { + str[j] = hex2dec(str + i + 2); + i += 4; + } + else { + str[j] = str[i++]; + } + } + *len = j; + + while (j < i) { + str[j++] = '\0'; + } + + return str; +} + +static char *escape(unsigned char *str, apr_size_t *len) +{ + char *new = apr_pcalloc(g_mp, (*len * 4) + 1); + apr_size_t i, j; + for (i = j = 0; i < *len; i++) { + if ((str[i] >= 0x20) && (str[i] <= 0x7e)) { + new[j++] = str[i]; + } + else { + sprintf(new + j, "\\x%02x", str[i]); + j += 4; + } + } + *len = j; + + return new; +} + + +/* Testing functions */ + +static int init_tfn(tfn_data_t *data, const char *name, unsigned char *input, apr_size_t input_len, char **errmsg) { + *errmsg = NULL; + + data->name = name; + data->input = apr_pmemdup(g_mp, input, input_len); + data->input_len = input_len; + data->metadata = msre_engine_tfn_resolve(modsecurity->msre, name); + if (data->metadata == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to fetch tfn \"%s\".", name); + return -1; + } + + return 0; +} + +static int test_tfn(tfn_data_t *data, unsigned char **rval, apr_size_t *rval_len, char **errmsg) +{ + int rc = -1; + + *errmsg = NULL; + + /* Execute the tfn */ + rc = data->metadata->execute(g_mp, data->input, (long)(data->input_len), (char **)rval, (long *)rval_len); + if (rc < 0) { + *errmsg = apr_psprintf(g_mp, "Failed to execute tfn \"%s\".", data->name); + } + + return rc; +} + +static int init_op(op_data_t *data, const char *name, const char *param, unsigned char *input, apr_size_t input_len, char **errmsg) { + const char *args = apr_psprintf(g_mp, "@%s %s", name, param); + char *conf_fn; + int rc = -1; + + *errmsg = NULL; + + data->name = name; + data->param = param; + data->input = input; + data->input_len = input_len; + + if ( apr_filepath_merge(&conf_fn, NULL, "t/unit-test.conf", APR_FILEPATH_TRUENAME, g_mp) != APR_SUCCESS) { + *errmsg = apr_psprintf(g_mp, "Failed to build a conf filename."); + return -1; + } + + /* Register UNIT_TEST variable */ + msre_engine_variable_register(modsecurity->msre, + "UNIT_TEST", + VAR_SIMPLE, + 0, 0, + NULL, + NULL, + VAR_DONT_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* Lookup the operator */ + data->metadata = msre_engine_op_resolve(modsecurity->msre, name); + if (data->metadata == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to fetch op \"%s\".", name); + return -1; + } + + /* Create a ruleset/rule */ + data->ruleset = msre_ruleset_create(modsecurity->msre, g_mp); + if (data->ruleset == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to create ruleset for op \"%s\".", name); + return -1; + } + data->rule = msre_rule_create(data->ruleset, RULE_TYPE_NORMAL, conf_fn, 1, "UNIT_TEST", args, DEFAULT_ACTION, errmsg); + if (data->rule == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to create rule for op \"%s\": %s", name, *errmsg); + return -1; + } + + /* Create a fake variable */ + data->var = (msre_var *)apr_pcalloc(g_mp, sizeof(msre_var)); + data->var->name = "UNIT_TEST"; + data->var->value = apr_pstrmemdup(g_mp, (char *)input, input_len); + data->var->value_len = input_len; + data->var->metadata = msre_resolve_var(modsecurity->msre, data->var->name); + if (data->var->metadata == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to resolve variable for op \"%s\": %s", name, data->var->name); + return -1; + } + + /* Initialize the operator parameter */ + if (data->metadata->param_init != NULL) { + rc = data->metadata->param_init(data->rule, errmsg); + if (rc <= 0) { + *errmsg = apr_psprintf(g_mp, "Failed to init op \"%s\": %s", name, *errmsg); + return rc; + } + } + + return 0; +} + +static int test_op(op_data_t *data, char **errmsg) +{ + int rc = -1; + + *errmsg = NULL; + + /* Execute the operator */ + if (data->metadata->execute != NULL) { + rc = data->metadata->execute(g_msr, data->rule, data->var, errmsg); + if (rc < 0) { + *errmsg = apr_psprintf(g_mp, "Failed to execute op \"%s\": %s", data->name, *errmsg); + } + } + + return rc; +} + +static int init_action(action_data_t *data, const char *name, const char *param, char **errmsg) +{ + const char *action_string = NULL; + char *conf_fn; + + *errmsg = NULL; + + if ((param == NULL) || (strcmp("", param) == 0)) { + action_string = apr_psprintf(g_mp, "%s", name); + } + else { + action_string = apr_psprintf(g_mp, "%s:%s", name, param); + } + if (action_string == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to build action string for action: \"%s\".", name); + return -1; + } + + if ( apr_filepath_merge(&conf_fn, NULL, "t/unit-test.conf", APR_FILEPATH_TRUENAME, g_mp) != APR_SUCCESS) { + *errmsg = apr_psprintf(g_mp, "Failed to build a conf filename."); + return -1; + } + + /* Register UNIT_TEST variable */ + msre_engine_variable_register(modsecurity->msre, + "UNIT_TEST", + VAR_SIMPLE, + 0, 0, + NULL, + NULL, + VAR_DONT_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* Create a ruleset/rule */ + data->ruleset = msre_ruleset_create(modsecurity->msre, g_mp); + if (data->ruleset == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to create ruleset for action \"%s\".", name); + return -1; + } + data->rule = msre_rule_create(data->ruleset, RULE_TYPE_NORMAL, conf_fn, 1, "UNIT_TEST", "@unconditionalMatch", action_string, errmsg); + if (data->rule == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to create rule for action \"%s\": %s", name, *errmsg); + return -1; + } + + /* Get the actionset/action */ + data->actionset = data->rule->actionset; + if (data->actionset == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to fetch actionset for action \"%s\"", name); + return -1; + } + data->action = (msre_action *)apr_table_get(data->actionset->actions, name); + if (data->action == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to fetch action for action \"%s\"", name); + return -1; + } + + return 0; +} + +static int test_action(action_data_t *data, char **errmsg) +{ + int rc = -1; + + *errmsg = NULL; + + /* Execute the action */ + if (data->action->metadata->execute != NULL) { + rc = data->action->metadata->execute(g_msr, g_mp, data->rule, data->action); + if (rc < 0) { + *errmsg = apr_psprintf(g_mp, "Failed to execute action \"%s\": %d", data->name, rc); + } + } + + return rc; +} + + +/* Initialization */ +static void init_msr(void) +{ + directory_config *dcfg = NULL; + request_rec *r = NULL; + r = (request_rec *)apr_pcalloc(g_mp, sizeof(request_rec)); + + dcfg = (directory_config *)apr_pcalloc(g_mp, sizeof(directory_config)); + dcfg->is_enabled = 0; + dcfg->reqbody_access = 0; + dcfg->reqbody_buffering = 0; + dcfg->reqbody_inmemory_limit = REQUEST_BODY_DEFAULT_INMEMORY_LIMIT; + dcfg->reqbody_limit = REQUEST_BODY_DEFAULT_LIMIT; + dcfg->reqbody_no_files_limit = REQUEST_BODY_NO_FILES_DEFAULT_LIMIT; + dcfg->resbody_access = 0; + dcfg->of_limit = RESPONSE_BODY_DEFAULT_LIMIT; + dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT; + dcfg->debuglog_fd = NOT_SET_P; + dcfg->debuglog_name = "msc-test-debug.log"; + dcfg->debuglog_level = debuglog_level; + dcfg->cookie_format = 0; + dcfg->argument_separator = '&'; + dcfg->rule_inheritance = 0; + dcfg->auditlog_flag = 0; + dcfg->auditlog_type = AUDITLOG_SERIAL; + dcfg->auditlog_fd = NULL; + dcfg->auditlog2_fd = NULL; + dcfg->auditlog_name = NULL; + dcfg->auditlog2_name = NULL; + dcfg->auditlog_storage_dir = NULL; + dcfg->auditlog_parts = "ABCFHZ"; + dcfg->auditlog_relevant_regex = NULL; + dcfg->tmp_dir = guess_tmp_dir(g_mp); + dcfg->upload_dir = NULL; + dcfg->upload_keep_files = KEEP_FILES_OFF; + dcfg->upload_validates_files = 0; + dcfg->data_dir = "."; + dcfg->webappid = "default"; + dcfg->content_injection_enabled = 0; + dcfg->pdfp_enabled = 0; + dcfg->pdfp_secret = NULL; + dcfg->pdfp_timeout = 10; + dcfg->pdfp_token_name = "PDFPTOKEN"; + dcfg->pdfp_only_get = 1; + dcfg->pdfp_method = PDF_PROTECT_METHOD_TOKEN_REDIRECTION; + dcfg->geo = NULL; + dcfg->cache_trans = MODSEC_CACHE_ENABLED; + dcfg->cache_trans_min = 15; + dcfg->cache_trans_max = 0; + dcfg->request_encoding = NULL; + + g_msr = (modsec_rec *)apr_pcalloc(g_mp, sizeof(modsec_rec)); + g_msr->modsecurity = modsecurity; + g_msr->mp = g_mp; + g_msr->r = r; + g_msr->r_early = r; + g_msr->request_time = apr_time_now(); + g_msr->dcfg1 = NULL; + g_msr->usercfg = NULL; + g_msr->txcfg = dcfg; + g_msr->txid = "FAKE-TXID"; + g_msr->error_messages = NULL; + g_msr->alerts = NULL; + g_msr->server_software = "FAKE-SERVER-SOFTWARE"; + g_msr->local_addr = "127.0.0.1"; + g_msr->local_port = 80; + g_msr->remote_addr = "127.0.0.1"; + g_msr->remote_port = 1080; + g_msr->request_line = "GET /unit-tests HTTP/1.1"; + g_msr->request_uri = "http://localhost/unit-tests"; + g_msr->request_method = "GET"; + g_msr->query_string = ""; + g_msr->request_protocol = "HTTP/1.1"; + g_msr->request_headers = NULL; + g_msr->hostname = "localhost"; + g_msr->msc_rule_mptmp = g_mp; + g_msr->tx_vars = apr_table_make(g_mp, 1); + g_msr->collections_original = apr_table_make(g_mp, 1); + g_msr->collections = apr_table_make(g_mp, 1); + g_msr->collections_dirty = apr_table_make(g_mp, 1); +} + +/** + * Usage text. + */ +static void usage(void) +{ + fprintf(stderr, "ModSecurity Unit Tester v%s\n", MODSEC_VERSION); + fprintf(stderr, " Usage: msc_test [options]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " Options:\n"); + fprintf(stderr, " -t Type (required)\n"); + fprintf(stderr, " -n Name (required)\n"); + fprintf(stderr, " -p Parameter (required)\n"); + fprintf(stderr, " -P Prerun (optional for actions)\n"); + fprintf(stderr, " -r Function return code (required for some types)\n"); + fprintf(stderr, " -I Iterations (default 1)\n"); + fprintf(stderr, " -D Debug log level (default 0)\n"); + fprintf(stderr, " -N No input on stdin.\n\n"); + fprintf(stderr, " -h This help\n\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Input is from stdin unless -N is used.\n"); +} + + + +/* Main */ + +int main(int argc, const char * const argv[]) +{ + apr_getopt_t *opt; + apr_file_t *fd; + apr_size_t nbytes = 0; + const char *type = NULL; + const char *name = NULL; + unsigned char *param = NULL; + unsigned char *prerun = NULL; + const char *returnval = NULL; + int iterations = 1; + char *errmsg = NULL; + unsigned char *out = NULL; + apr_size_t param_len = 0; + apr_size_t prerun_len = 0; + apr_size_t out_len = 0; + int noinput = 0; + int rc = 0; + int result = 0; + int ec = 0; + int i; + apr_time_t T0 = 0; + apr_time_t T1 = 0; + tfn_data_t tfn_data; + op_data_t op_data; + action_data_t action_data; + int ret = 0; + + memset(&tfn_data, 0, sizeof(tfn_data_t)); + memset(&op_data, 0, sizeof(op_data_t)); + memset(&action_data, 0, sizeof(action_data_t)); + + apr_app_initialize(&argc, &argv, NULL); + atexit(apr_terminate); + + apr_pool_create(&g_mp, NULL); + + rc = apr_getopt_init(&opt, g_mp, argc, argv); + if (rc != APR_SUCCESS) { + fprintf(stderr, "Failed to initialize.\n\n"); + usage(); + exit(1); + } + + do { + char ch; + const char *val; + rc = apr_getopt(opt, CMDLINE_OPTS, &ch, &val); + switch (rc) { + case APR_SUCCESS: + switch (ch) { + case 't': + type = val; + break; + case 'n': + name = val; + break; + case 'p': + param_len = strlen(val); + param = apr_pmemdup(g_mp, val, param_len + 1); + unescape_inplace(param, ¶m_len); + break; + case 'P': + prerun_len = strlen(val); + prerun = apr_pmemdup(g_mp, val, prerun_len + 1); + unescape_inplace(prerun, &prerun_len); + break; + case 'r': + returnval = val; + break; + case 'I': + iterations = atoi(val); + break; + case 'D': + debuglog_level = atoi(val); + break; + case 'N': + noinput = 1; + break; + case 'h': + usage(); + exit(0); + } + break; + case APR_BADCH: + case APR_BADARG: + usage(); + exit(1); + } + } while (rc != APR_EOF); + + rc = apr_getopt_init(&opt, g_mp, argc, argv); + if (!type || !name || !param) { + usage(); + exit(1); + } + + modsecurity = modsecurity_create(g_mp, MODSEC_OFFLINE); + test_name = apr_psprintf(g_mp, "%s/%s", type, name); + + if (noinput == 0) { + if (apr_file_open_stdin(&fd, g_mp) != APR_SUCCESS) { + fprintf(stderr, "Failed to open stdin\n"); + exit(1); + } + + /* Read in the input */ + nbytes = BUFLEN; + memset(buf, 0, nbytes); + rc = apr_file_read(fd, buf, &nbytes); + if ((rc != APR_EOF) && (rc != APR_SUCCESS)) { + fprintf(stderr, "Failed to read data\n"); + exit(1); + } + + if (nbytes < 0) { + fprintf(stderr, "Error reading data\n"); + exit(1); + } + + apr_file_close(fd); + } + + if (strcmp("tfn", type) == 0) { + ret = returnval ? atoi(returnval) : -8888; + + rc = init_tfn(&tfn_data, name, buf, nbytes, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: %s\n", errmsg); + result = RESULT_ERROR; + } + } + else if (strcmp("op", type) == 0) { + if (!returnval) { + fprintf(stderr, "Return value required for type \"%s\"\n", type); + exit(1); + } + ret = atoi(returnval); + + init_msr(); + + rc = init_op(&op_data, name, (const char *)param, buf, nbytes, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: %s\n", errmsg); + result = RESULT_ERROR; + } + } + else if (strcmp("action", type) == 0) { + if (!returnval) { + fprintf(stderr, "Return value required for type \"%s\"\n", type); + exit(1); + } + ret = atoi(returnval); + + init_msr(); + + if (prerun) { + action_data_t paction_data; + char *pname = apr_pstrdup(g_mp, (const char *)prerun); + char *pparam = NULL; + + if ((pparam = strchr((const char *)pname, ':'))) { + pparam[0] = '\0'; + pparam++; + } + + rc = init_action(&paction_data, pname, (const char *)pparam, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: prerun - %s\n", errmsg); + exit(1); + } + + rc = test_action(&paction_data, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: prerun - %s\n", errmsg); + exit(1); + } + } + + rc = init_action(&action_data, name, (const char *)param, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: %s\n", errmsg); + result = RESULT_ERROR; + } + } + + if (iterations > 1) { + apr_time_clock_hires (g_mp); + T0 = apr_time_now(); + } + + for (i = 1; i <= iterations; i++) { + #ifdef VERBOSE + if (i % 100 == 0) { + if (i == 100) { + fprintf(stderr, "Iterations/100: ."); + } + else { + fprintf(stderr, "."); + } + } + #endif + + if (strcmp("tfn", type) == 0) { + /* Transformations */ + rc = test_tfn(&tfn_data, &out, &out_len, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: %s\n", errmsg); + result = RESULT_ERROR; + } + else if ((ret != -8888) && (rc != ret)) { + fprintf(stderr, "Returned %d (expected %d)\n", rc, ret); + result = RESULT_WRONGRET; + } + else if (param_len != out_len) { + fprintf(stderr, "Length %" APR_SIZE_T_FMT " (expected %" APR_SIZE_T_FMT ")\n", out_len, param_len); + result = RESULT_WRONGSIZE; + } + else { + result = memcmp(param, out, param_len) ? RESULT_MISMATCHED : RESULT_SUCCESS; + } + + if (result != RESULT_SUCCESS) { + apr_size_t s0len = nbytes; + const char *s0 = escape(buf, &s0len); + apr_size_t s1len = out_len; + const char *s1 = escape(out, &s1len); + apr_size_t s2len = param_len; + const char *s2 = escape(param, &s2len); + + fprintf(stderr, " Input: '%s' len=%" APR_SIZE_T_FMT "\n" + "Output: '%s' len=%" APR_SIZE_T_FMT "\n" + "Expect: '%s' len=%" APR_SIZE_T_FMT "\n", + s0, nbytes, s1, out_len, s2, param_len); + ec = 1; + } + } + else if (strcmp("op", type) == 0) { + /* Operators */ + rc = test_op(&op_data, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: %s\n", errmsg); + result = RESULT_ERROR; + } + else if (rc != ret) { + fprintf(stderr, "Returned %d (expected %d)\n", rc, ret); + result = RESULT_WRONGRET; + } + else { + result = RESULT_SUCCESS; + } + + if (result != RESULT_SUCCESS) { + apr_size_t s0len = nbytes; + const char *s0 = escape(buf, &s0len); + + fprintf(stderr, " Test: '@%s %s'\n" + "Input: '%s' len=%" APR_SIZE_T_FMT "\n", + name, param, s0, nbytes); + ec = 1; + } + } + else if (strcmp("action", type) == 0) { + /* Actions */ + int n; + const apr_array_header_t *arr; + apr_table_entry_t *te; + + rc = test_action(&action_data, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: %s\n", errmsg); + result = RESULT_ERROR; + } + else if (rc != ret) { + fprintf(stderr, "Returned %d (expected %d)\n", rc, ret); + result = RESULT_WRONGRET; + } + else { + result = RESULT_SUCCESS; + } + + if (result != RESULT_SUCCESS) { + fprintf(stderr, " Test: '%s:%s'\n" + "Prerun: '%s'\n", + name, param, (prerun ? (const char *)prerun : "")); + ec = 1; + } + + /* Store any collections that were initialized and changed */ + arr = apr_table_elts(g_msr->collections); + te = (apr_table_entry_t *)arr->elts; + for (n = 0; n < arr->nelts; n++) { + apr_table_t *col = (apr_table_t *)te[n].val; +// apr_table_t *orig_col = NULL; + + if (g_msr->txcfg->debuglog_level >= 9) { + msr_log(g_msr, 9, "Found loaded collection: %s", te[n].key); + } + /* Only store those collections that changed. */ + if (apr_table_get(g_msr->collections_dirty, te[n].key)) { + int x = collection_store(g_msr, col); + + if (g_msr->txcfg->debuglog_level >= 9) { + msr_log(g_msr, 9, "Stored collection: %s (%d)", te[n].key, x); + } + } +#if 0 + /* Re-populate the original values with the new ones. */ + if ((orig_col = (apr_table_t *)apr_table_get(g_msr->collections_original, te[n].key)) != NULL) { + const apr_array_header_t *orig_arr = apr_table_elts(orig_col); + apr_table_entry_t *orig_te = (apr_table_entry_t *)orig_arr->elts; + int m; + + for (m = 0; m < orig_arr->nelts; m++) { + msc_string *mstr = (msc_string *)apr_table_get(col, orig_te[m].key); + + if (g_msr->txcfg->debuglog_level >= 9) { + msr_log(g_msr, 9, "Updating original collection: %s.%s=%s", te[n].key, mstr->name, mstr->value); + } + //apr_table_setn(orig_col, orig_te[m].key, (void *)mstr ); + collection_original_setvar(g_msr, te[n].key, mstr); + + + } + } +#endif + } + apr_table_clear(g_msr->collections_dirty); + apr_table_clear(g_msr->collections_original); + } + else { + fprintf(stderr, "Unknown type: \"%s\"\n", type); + exit(1); + } + + if (ec != 0) { + fprintf(stdout, "%s\n", errmsg ? errmsg : ""); + return ec; + } + } + + if (iterations > 1) { + double dT; + T1 = apr_time_now(); + + dT = apr_time_as_msec(T1 - T0); + + #ifdef VERBOSE + if (i >= 100) { + fprintf(stderr, "\n"); + } + #endif + + fprintf(stdout, "%d @ %.4f msec per iteration.\n", iterations, dT / iterations); + } + fprintf(stdout, "%s\n", errmsg ? errmsg : ""); + + return ec; +} + + diff --git a/2.5.13/2.5.x/apache2/msc_util.c b/2.5.13/2.5.x/apache2/msc_util.c new file mode 100644 index 00000000..985934a8 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_util.c @@ -0,0 +1,1628 @@ +/* + * 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 +#include +#include +#include +#include + +#include "mod_security2_config.h" +#include "msc_release.h" +#include "msc_util.h" + +#include + +/** + * NOTE: Be careful as these can ONLY be used on static values for X. + * (i.e. VALID_HEX(c++) will NOT work) + */ +#define VALID_HEX(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F'))) +#define ISODIGIT(X) ((X >= '0')&&(X <= '7')) + +#if (defined(WIN32) || defined(NETWARE)) +/** Windows does not define all the octal modes */ +#define S_IXOTH 00001 +#define S_IWOTH 00002 +#define S_IROTH 00004 +#define S_IXGRP 00010 +#define S_IWGRP 00020 +#define S_IRGRP 00040 +#define S_IXUSR 00100 +#define S_IWUSR 00200 +#define S_IRUSR 00400 +#define S_ISVTX 01000 +#define S_ISGID 02000 +#define S_ISUID 04000 +#endif /* defined(WIN32 || NETWARE) */ + +/* Base64 tables used in decodeBase64Ext */ +static const char b64_pad = '='; + +static const short b64_reverse_t[256] = { + -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -2, -1, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2, + -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2, + -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 +}; + + +/** + * + */ +int parse_boolean(const char *input) { + if (input == NULL) return -1; + if (strcasecmp(input, "on") == 0) return 1; + if (strcasecmp(input, "true") == 0) return 1; + if (strcasecmp(input, "1") == 0) return 1; + if (strcasecmp(input, "off") == 0) return 0; + if (strcasecmp(input, "false") == 0) return 0; + if (strcasecmp(input, "0") == 0) return 0; + + return -1; +} + +/* \brief Decode Base64 data with special chars +* +* \param plain_text Pointer to plain text data +* \param input Pointer to input data +* \param input_len Input data length +* +* \retval 0 On failure +* \retval string length On Success +*/ +int decode_base64_ext(char *plain_text, const char *input, int input_len) +{ + const char *encoded = input; + int i = 0, j = 0, k = 0; + int ch = 0; + + while ((ch = *encoded++) != '\0' && input_len-- > 0) { + if (ch == b64_pad) { + if (*encoded != '=' && (i % 4) == 1) { + return 0; + } + continue; + } + + ch = b64_reverse_t[ch]; + if (ch < 0 || ch == -1) { + continue; + } else if (ch == -2) { + return 0; + } + switch(i % 4) { + case 0: + plain_text[j] = ch << 2; + break; + case 1: + plain_text[j++] |= ch >> 4; + plain_text[j] = (ch & 0x0f) << 4; + break; + case 2: + plain_text[j++] |= ch >>2; + plain_text[j] = (ch & 0x03) << 6; + break; + case 3: + plain_text[j++] |= ch; + break; + } + i++; + } + + k = j; + if (ch == b64_pad) { + switch(i % 4) { + case 1: + return 0; + case 2: + k++; + case 3: + plain_text[k] = 0; + } + } + + plain_text[j] = '\0'; + + return j; +} + + +/** + * Parses a string that contains a name-value pair in the form "name=value". + * IMP1 It does not check for whitespace between tokens. + */ +int parse_name_eq_value(apr_pool_t *mp, const char *input, char **name, char **value) { + char *p = NULL; + + if ((name == NULL)||(value == NULL)) return -1; + if (input == NULL) return 0; + + *name = NULL; + *value = NULL; + p = (char *)input; + + while((*p != '=')&&(*p != '\0')) p++; + if (*p == '\0') { + *name = (char *)input; + return 1; + } + + *name = apr_pstrmemdup(mp, input, p - input); + if (*name == NULL) return -1; + p++; + + *value = apr_pstrdup(mp, p); + if (*value == NULL) return -1; + + return 1; +} + +/** + * + * IMP1 Assumes NUL-terminated + */ +char *url_encode(apr_pool_t *mp, char *input, unsigned int input_len, int *changed) { + char *rval, *d; + unsigned int i, len; + + *changed = 0; + + len = input_len * 3 + 1; + d = rval = apr_palloc(mp, len); + if (rval == NULL) return NULL; + + /* ENH Only encode the characters that really need to be encoded. */ + + for(i = 0; i < input_len; i++) { + unsigned char c = input[i]; + + if (c == ' ') { + *d++ = '+'; + *changed = 1; + } else + if ( (c == 42) || ((c >= 48)&&(c <= 57)) || ((c >= 65)&&(c <= 90)) + || ((c >= 97)&&(c <= 122)) + ) { + *d++ = c; + } else { + *d++ = '%'; + c2x(c, (unsigned char *)d); + d += 2; + *changed = 1; + } + } + + *d = '\0'; + + return rval; +} + +/** + * Appends an URL-encoded version of the source string to the + * destination string, but makes sure that no more than "maxlen" + * bytes are added. + */ +char *strnurlencat(char *destination, char *source, unsigned int maxlen) { + char *s = source; + char *d = destination; + + /* ENH Only encode the characters that really need to be encoded. */ + + /* Advance to the end of destination string. */ + while(*d != '\0') d++; + + /* Loop while there's bytes in the source string or + * until we reach the output limit. + */ + while((*s != '\0')&&(maxlen > 0)) { + unsigned char c = *s; + + if (c == ' ') { + *d++ = '+'; + maxlen--; + } else + if ( (c == 42) || ((c >= 48)&&(c <= 57)) || ((c >= 65)&&(c <= 90)) + || ((c >= 97)&&(c <= 122)) + ) { + *d++ = c; + maxlen--; + } else { + if (maxlen >= 3) { + *d++ = '%'; + c2x(c, (unsigned char *)d); + d += 2; + maxlen -= 3; + } else { + /* If there's not enough room for the encoded + * byte we ignore it. + */ + maxlen = 0; + } + } + + s++; + } + + *d++ = '\0'; + + return destination; +} + +/** + * + */ +char *file_basename(apr_pool_t *mp, const char *filename) { + char *d, *p; + + if (filename == NULL) return NULL; + d = apr_pstrdup(mp, filename); + if (d == NULL) return NULL; + + p = strrchr(d, '/'); + if (p != NULL) d = p + 1; + p = strrchr(d, '\\'); + if (p != NULL) d = p + 1; + + return d; +} + +/** + * + */ +#ifdef WIN32 +char *file_dirname(apr_pool_t *p, const char *filename) { + char *b, *c, *d; + + if (filename == NULL) return NULL; + b = apr_pstrdup(p, filename); + if (b == NULL) return NULL; + + c = strrchr(b, '/'); + if (c != NULL) { + d = strrchr(c, '\\'); + if (d != NULL) *d = '\0'; + else *c = '\0'; + } else { + d = strrchr(b, '\\'); + if (d != NULL) *d = '\0'; + } + + return b; +} +#else +char *file_dirname(apr_pool_t *p, const char *filename) { + char *b, *c; + + if (filename == NULL) return NULL; + b = apr_pstrdup(p, filename); + if (b == NULL) return NULL; + + c = strrchr(b, '/'); + if (c != NULL) *c = '\0'; + + return b; +} +#endif + + +/** + * + */ +int hex2bytes_inplace(unsigned char *data, int len) { + unsigned char *d = data; + int i, count = 0; + + if ((data == NULL)||(len == 0)) return 0; + + for(i = 0; i <= len - 2; i += 2) { + *d++ = x2c(&data[i]); + count++; + } + *d = '\0'; + + return count; +} + +/** + * Converts a series of bytes into its hexadecimal + * representation. + */ +char *bytes2hex(apr_pool_t *pool, unsigned char *data, int len) { + static unsigned char b2hex[] = "0123456789abcdef"; + char *hex = NULL; + int i, j; + + hex = apr_palloc(pool, (len * 2) + 1); + if (hex == NULL) return NULL; + + j = 0; + for(i = 0; i < len; i++) { + hex[j++] = b2hex[data[i] >> 4]; + hex[j++] = b2hex[data[i] & 0x0f]; + } + hex[j] = 0; + + return hex; +} + +/** + * + */ +int is_token_char(unsigned char c) { + /* ENH Is the performance important at all? We could use a table instead. */ + + /* CTLs not allowed */ + if ((c <= 32)||(c >= 127)) return 0; + + switch(c) { + case '(' : + case ')' : + case '<' : + case '>' : + case '@' : + case ',' : + case ';' : + case ':' : + case '\\' : + case '"' : + case '/' : + case '[' : + case ']' : + case '?' : + case '=' : + return 0; + } + + return 1; +} + +/** + * + */ +int remove_lf_crlf_inplace(char *text) { + char *p = text; + int count = 0; + + if (text == NULL) return -1; + + while(*p != '\0') { + count++; + p++; + } + + if (count > 0) { + if (*(p - 1) == '\n') { + *(p - 1) = '\0'; + if (count > 1) { + if (*(p - 2) == '\r') { + *(p - 2) = '\0'; + } + } + } + } + + return 1; +} + +/** + * Converts a byte given as its hexadecimal representation + * into a proper byte. Handles uppercase and lowercase letters + * but does not check for overflows. + */ +unsigned char x2c(unsigned char *what) { + register unsigned char digit; + + digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); + digit *= 16; + digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0')); + + return digit; +} + +/** + * Converts a single hexadecimal digit into a decimal value. + */ +unsigned char xsingle2c(unsigned char *what) { + register unsigned char digit; + + digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); + + return digit; +} + +/** + * + */ +char *guess_tmp_dir(apr_pool_t *p) { + char *filename = NULL; + + /* ENH Use apr_temp_dir_get instead. */ + + #ifdef WIN32 + filename = apr_pcalloc(p, 256); + if (filename == NULL) return ""; + if (GetTempPath(255, filename) != 0) return filename; + #endif + + filename = getenv("TMPDIR"); + if (filename != NULL) return filename; + + filename = getenv("TEMP"); + if (filename != NULL) return filename; + + filename = getenv("TMP"); + if (filename != NULL) return filename; + + #if defined NETWARE + return("sys:/tmp/"); + #elif defined WIN32 + return(""); + #else + return("/tmp/"); + #endif +} + +/** + * + */ +char *current_logtime(apr_pool_t *mp) { + apr_time_exp_t t; + char tstr[100]; + apr_size_t len; + + apr_time_exp_lt(&t, apr_time_now()); + + apr_strftime(tstr, &len, 80, "%d/%b/%Y:%H:%M:%S ", &t); + apr_snprintf(tstr + strlen(tstr), 80 - strlen(tstr), "%c%.2d%.2d", + t.tm_gmtoff < 0 ? '-' : '+', + t.tm_gmtoff / (60 * 60), t.tm_gmtoff % (60 * 60)); + return apr_pstrdup(mp, tstr); +} + +/** + * + */ +char *current_filetime(apr_pool_t *mp) { + apr_time_exp_t t; + char tstr[100]; + apr_size_t len; + + apr_time_exp_lt(&t, apr_time_now()); + + apr_strftime(tstr, &len, 80, "%Y%m%d-%H%M%S", &t); + return apr_pstrdup(mp, tstr); +} + +/** + * + */ +int msc_mkstemp_ex(char *template, int mode) { + int fd = -1; + + /* ENH Use apr_file_mktemp instead. */ + +#if !(defined(WIN32)||defined(NETWARE)) + fd = mkstemp(template); +#ifdef HAVE_FCHMOD + if ((fd != -1) && (mode != 0)) { + if (fchmod(fd, mode) == -1) { + return -1; + } + } +#endif /* HAVE_FCHMOD */ +#else + if (mktemp(template) == NULL) return -1; + fd = open(template, O_WRONLY | O_APPEND | O_CREAT | O_BINARY, mode); +#endif /* !(defined(WIN32)||defined(NETWARE)) */ + + return fd; +} + +/** + * + */ +int msc_mkstemp(char *template) { + return msc_mkstemp_ex(template, CREATEMODE_UNISTD); +} + +/** + * Converts the input string to lowercase (in-place). + */ +char *strtolower_inplace(unsigned char *str) { + unsigned char *c = str; + + if (str == NULL) return NULL; + + while(*c != 0) { + *c = tolower(*c); + c++; + } + + return (char *)str; +} + +/** + * Converts a single byte into its hexadecimal representation. + * Will overwrite two bytes at the destination. + */ +unsigned char *c2x(unsigned what, unsigned char *where) { + static const char c2x_table[] = "0123456789abcdef"; + + what = what & 0xff; + *where++ = c2x_table[what >> 4]; + *where++ = c2x_table[what & 0x0f]; + + return where; +} + +char *log_escape(apr_pool_t *mp, const char *text) { + return _log_escape(mp, (const unsigned char *)text, text ? strlen(text) : 0, 1, 0); +} + +char *log_escape_nq(apr_pool_t *mp, const char *text) { + return _log_escape(mp, (const unsigned char *)text, text ? strlen(text) : 0, 0, 0); +} + +char *log_escape_ex(apr_pool_t *mp, const char *text, unsigned long int text_length) { + return _log_escape(mp, (const unsigned char *)text, text_length, 1, 0); +} + +char *log_escape_nq_ex(apr_pool_t *mp, const char *text, unsigned long int text_length) { + return _log_escape(mp, (const unsigned char *)text, text_length, 0, 0); +} + +char *log_escape_header_name(apr_pool_t *mp, const char *text) { + return _log_escape(mp, (const unsigned char *)text, text ? strlen(text) : 0, 0, 1); +} + +char *log_escape_raw(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length) { + unsigned char *ret = apr_palloc(mp, text_length * 4 + 1); + unsigned long int i, j; + + for (i = 0, j = 0; i < text_length; i++, j += 4) { + ret[j] = '\\'; + ret[j+1] = 'x'; + c2x(text[i], ret+j+2); + } + ret[text_length * 4] = '\0'; + + return (char *)ret; +} + +char *log_escape_nul(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length) { + unsigned char *ret = apr_palloc(mp, text_length * 4 + 1); + unsigned long int i, j; + + for (i = 0, j = 0; i < text_length; i++) { + if (text[i] == '\0') { + ret[j] = '\\'; + ret[j+1] = 'x'; + c2x(text[i], ret+j+2); + j += 4; + } + else { + ret[j] = text[i]; + j++; + } + } + ret[j] = '\0'; + + return (char *)ret; +} + +/** + * Transform text to ASCII printable or hex escaped + */ +char *log_escape_hex(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length) { + unsigned char *ret = apr_palloc(mp, text_length * 4 + 1); + unsigned long int i, j; + + for (i = 0, j = 0; i < text_length; i++) { + if ( (text[i] == '"') + ||(text[i] == '\\') + ||(text[i] <= 0x1f) + ||(text[i] >= 0x7f)) + { + ret[j] = '\\'; + ret[j+1] = 'x'; + c2x(text[i], ret+j+2); + j += 4; + } + else { + ret[j] = text[i]; + j ++; + } + } + ret[j] = '\0'; + + return (char *)ret; +} + +/** + * Transform input into a form safe for logging. + */ +char *_log_escape(apr_pool_t *mp, const unsigned char *input, unsigned long int input_len, + int escape_quotes, int escape_colon) +{ + unsigned char *d = NULL; + char *ret = NULL; + unsigned long int i; + + if (input == NULL) return NULL; + + ret = apr_palloc(mp, input_len * 4 + 1); + if (ret == NULL) return NULL; + d = (unsigned char *)ret; + + i = 0; + while(i < input_len) { + switch(input[i]) { + case ':' : + if (escape_colon) { + *d++ = '\\'; + *d++ = ':'; + } else { + *d++ = input[i]; + } + break; + case '"' : + if (escape_quotes) { + *d++ = '\\'; + *d++ = '"'; + } else { + *d++ = input[i]; + } + break; + case '\b' : + *d++ = '\\'; + *d++ = 'b'; + break; + case '\n' : + *d++ = '\\'; + *d++ = 'n'; + break; + case '\r' : + *d++ = '\\'; + *d++ = 'r'; + break; + case '\t' : + *d++ = '\\'; + *d++ = 't'; + break; + case '\v' : + *d++ = '\\'; + *d++ = 'v'; + break; + case '\\' : + *d++ = '\\'; + *d++ = '\\'; + break; + default : + if ((input[i] <= 0x1f)||(input[i] >= 0x7f)) { + *d++ = '\\'; + *d++ = 'x'; + c2x(input[i], d); + d += 2; + } else { + *d++ = input[i]; + } + break; + } + + i++; + } + + *d = 0; + + return ret; +} + +/** + * JavaScript decoding. + * IMP1 Assumes NUL-terminated + */ + +int js_decode_nonstrict_inplace(unsigned char *input, long int input_len) { + unsigned char *d = (unsigned char *)input; + long int i, count; + + if (input == NULL) return -1; + + i = count = 0; + while (i < input_len) { + if (input[i] == '\\') { + /* Character is an escape. */ + + if ( (i + 5 < input_len) && (input[i + 1] == 'u') + && (VALID_HEX(input[i + 2])) && (VALID_HEX(input[i + 3])) + && (VALID_HEX(input[i + 4])) && (VALID_HEX(input[i + 5])) ) + { + /* \uHHHH */ + + /* Use only the lower byte. */ + *d = x2c(&input[i + 4]); + + /* Full width ASCII (ff01 - ff5e) needs 0x20 added */ + if ( (*d > 0x00) && (*d < 0x5f) + && ((input[i + 2] == 'f') || (input[i + 2] == 'F')) + && ((input[i + 3] == 'f') || (input[i + 3] == 'F'))) + { + (*d) += 0x20; + } + + d++; + count++; + i += 6; + } + else if ( (i + 3 < input_len) && (input[i + 1] == 'x') + && VALID_HEX(input[i + 2]) && VALID_HEX(input[i + 3])) { + /* \xHH */ + *d++ = x2c(&input[i + 2]); + count++; + i += 4; + } + else if ((i + 1 < input_len) && ISODIGIT(input[i + 1])) { + /* \OOO (only one byte, \000 - \377) */ + char buf[4]; + int j = 0; + + while((i + 1 + j < input_len)&&(j < 3)) { + buf[j] = input[i + 1 + j]; + j++; + if (!ISODIGIT(input[i + 1 + j])) break; + } + buf[j] = '\0'; + + if (j > 0) { + /* Do not use 3 characters if we will be > 1 byte */ + if ((j == 3) && (buf[0] > '3')) { + j = 2; + buf[j] = '\0'; + } + *d++ = (unsigned char)strtol(buf, NULL, 8); + i += 1 + j; + count++; + } + } + else if (i + 1 < input_len) { + /* \C */ + unsigned char c = input[i + 1]; + switch(input[i + 1]) { + case 'a' : + c = '\a'; + break; + case 'b' : + c = '\b'; + break; + case 'f' : + c = '\f'; + break; + case 'n' : + c = '\n'; + break; + case 'r' : + c = '\r'; + break; + case 't' : + c = '\t'; + break; + case 'v' : + c = '\v'; + break; + /* The remaining (\?,\\,\',\") are just a removal + * of the escape char which is default. + */ + } + + *d++ = c; + i += 2; + count++; + } + else { + /* Not enough bytes */ + while(i < input_len) { + *d++ = input[i++]; + count++; + } + } + } + else { + *d++ = input[i++]; + count++; + } + } + + *d = '\0'; + + return count; +} + +/** + * + * IMP1 Assumes NUL-terminated + */ +int urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len, int *changed) { + unsigned char *d = input; + long int i, count; + + *changed = 0; + + if (input == NULL) return -1; + + i = count = 0; + while (i < input_len) { + if (input[i] == '%') { + /* Character is a percent sign. */ + + if ((i + 1 < input_len)&&( (input[i + 1] == 'u')||(input[i + 1] == 'U') )) { + /* IIS-specific %u encoding. */ + if (i + 5 < input_len) { + /* We have at least 4 data bytes. */ + if ( (VALID_HEX(input[i + 2]))&&(VALID_HEX(input[i + 3])) + &&(VALID_HEX(input[i + 4]))&&(VALID_HEX(input[i + 5])) ) + { + /* We first make use of the lower byte here, ignoring the higher byte. */ + *d = x2c(&input[i + 4]); + + /* Full width ASCII (ff01 - ff5e) needs 0x20 added */ + if ( (*d > 0x00) && (*d < 0x5f) + && ((input[i + 2] == 'f') || (input[i + 2] == 'F')) + && ((input[i + 3] == 'f') || (input[i + 3] == 'F'))) + { + (*d) += 0x20; + } + + d++; + count++; + i += 6; + *changed = 1; + } else { + /* Invalid data, skip %u. */ + *d++ = input[i++]; + *d++ = input[i++]; + count += 2; + } + } else { + /* Not enough bytes (4 data bytes), skip %u. */ + *d++ = input[i++]; + *d++ = input[i++]; + count += 2; + } + } + else { + /* Standard URL encoding. */ + + /* Are there enough bytes available? */ + if (i + 2 < input_len) { + /* Yes. */ + + /* Decode a %xx combo only if it is valid. + */ + char c1 = input[i + 1]; + char c2 = input[i + 2]; + + if (VALID_HEX(c1) && VALID_HEX(c2)) { + *d++ = x2c(&input[i + 1]); + count++; + i += 3; + *changed = 1; + } else { + /* Not a valid encoding, skip this % */ + *d++ = input[i++]; + count++; + } + } else { + /* Not enough bytes available, skip this % */ + *d++ = input[i++]; + count++; + } + } + } + else { + /* Character is not a percent sign. */ + if (input[i] == '+') { + *d++ = ' '; + *changed = 1; + } else { + *d++ = input[i]; + } + + count++; + i++; + } + } + + *d = '\0'; + + return count; +} + +/** + * + * IMP1 Assumes NUL-terminated + */ +int urldecode_nonstrict_inplace_ex(unsigned char *input, long int input_len, int *invalid_count, int *changed) { + unsigned char *d = (unsigned char *)input; + long int i, count; + + *changed = 0; + + if (input == NULL) return -1; + + i = count = 0; + while (i < input_len) { + if (input[i] == '%') { + /* Character is a percent sign. */ + + /* Are there enough bytes available? */ + if (i + 2 < input_len) { + char c1 = input[i + 1]; + char c2 = input[i + 2]; + + if (VALID_HEX(c1) && VALID_HEX(c2)) { + /* Valid encoding - decode it. */ + *d++ = x2c(&input[i + 1]); + count++; + i += 3; + *changed = 1; + } else { + /* Not a valid encoding, skip this % */ + *d++ = input[i++]; + count ++; + (*invalid_count)++; + } + } else { + /* Not enough bytes available, copy the raw bytes. */ + *d++ = input[i++]; + count ++; + (*invalid_count)++; + } + } else { + /* Character is not a percent sign. */ + if (input[i] == '+') { + *d++ = ' '; + *changed = 1; + } else { + *d++ = input[i]; + } + count++; + i++; + } + } + + *d = '\0'; + + return count; +} + +/** + * + * IMP1 Assumes NUL-terminated + */ +int html_entities_decode_inplace(apr_pool_t *mp, unsigned char *input, int input_len) { + unsigned char *d = input; + int i, count; + + if ((input == NULL)||(input_len <= 0)) return 0; + + i = count = 0; + while((i < input_len)&&(count < input_len)) { + int z, copy = 1; + + /* Require an ampersand and at least one character to + * start looking into the entity. + */ + if ((input[i] == '&')&&(i + 1 < input_len)) { + int k, j = i + 1; + + if (input[j] == '#') { + /* Numerical entity. */ + copy++; + + if (!(j + 1 < input_len)) goto HTML_ENT_OUT; /* Not enough bytes. */ + j++; + + if ((input[j] == 'x')||(input[j] == 'X')) { + /* Hexadecimal entity. */ + copy++; + + if (!(j + 1 < input_len)) goto HTML_ENT_OUT; /* Not enough bytes. */ + j++; /* j is the position of the first digit now. */ + + k = j; + while((j < input_len)&&(isxdigit(input[j]))) j++; + if (j > k) { /* Do we have at least one digit? */ + /* Decode the entity. */ + char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k); + *d++ = (unsigned char)strtol(x, NULL, 16); + count++; + + /* Skip over the semicolon if it's there. */ + if ((j < input_len)&&(input[j] == ';')) i = j + 1; + else i = j; + + continue; + } else { + goto HTML_ENT_OUT; + } + } else { + /* Decimal entity. */ + k = j; + while((j < input_len)&&(isdigit(input[j]))) j++; + if (j > k) { /* Do we have at least one digit? */ + /* Decode the entity. */ + char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k); + *d++ = (unsigned char)strtol(x, NULL, 10); + count++; + + /* Skip over the semicolon if it's there. */ + if ((j < input_len)&&(input[j] == ';')) i = j + 1; + else i = j; + + continue; + } else { + goto HTML_ENT_OUT; + } + } + } else { + /* Text entity. */ + + k = j; + while((j < input_len)&&(isalnum(input[j]))) j++; + if (j > k) { /* Do we have at least one digit? */ + char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k); + + /* Decode the entity. */ + /* ENH What about others? */ + if (strcasecmp(x, "quot") == 0) *d++ = '"'; + else + if (strcasecmp(x, "amp") == 0) *d++ = '&'; + else + if (strcasecmp(x, "lt") == 0) *d++ = '<'; + else + if (strcasecmp(x, "gt") == 0) *d++ = '>'; + else + if (strcasecmp(x, "nbsp") == 0) *d++ = NBSP; + else { + /* We do no want to convert this entity, copy the raw data over. */ + copy = j - k + 1; + goto HTML_ENT_OUT; + } + + count++; + + /* Skip over the semicolon if it's there. */ + if ((j < input_len)&&(input[j] == ';')) i = j + 1; + else i = j; + + continue; + } + } + } + + HTML_ENT_OUT: + + for(z = 0; ((z < copy) && (count < input_len)); z++) { + *d++ = input[i++]; + count++; + } + } + + *d = '\0'; + + return count; +} + +/** + * + * IMP1 Assumes NUL-terminated + */ +int ansi_c_sequences_decode_inplace(unsigned char *input, int input_len) { + unsigned char *d = input; + int i, count; + + i = count = 0; + while(i < input_len) { + if ((input[i] == '\\')&&(i + 1 < input_len)) { + int c = -1; + + switch(input[i + 1]) { + case 'a' : + c = '\a'; + break; + case 'b' : + c = '\b'; + break; + case 'f' : + c = '\f'; + break; + case 'n' : + c = '\n'; + break; + case 'r' : + c = '\r'; + break; + case 't' : + c = '\t'; + break; + case 'v' : + c = '\v'; + break; + case '\\' : + c = '\\'; + break; + case '?' : + c = '?'; + break; + case '\'' : + c = '\''; + break; + case '"' : + c = '"'; + break; + } + + if (c != -1) i += 2; + + /* Hexadecimal or octal? */ + if (c == -1) { + if ((input[i + 1] == 'x')||(input[i + 1] == 'X')) { + /* Hexadecimal. */ + if ((i + 3 < input_len)&&(isxdigit(input[i + 2]))&&(isxdigit(input[i + 3]))) { + /* Two digits. */ + c = x2c(&input[i + 2]); + i += 4; + } else { + /* Invalid encoding, do nothing. */ + } + } + else + if (ISODIGIT(input[i + 1])) { /* Octal. */ + char buf[4]; + int j = 0; + + while((i + 1 + j < input_len)&&(j < 3)) { + buf[j] = input[i + 1 + j]; + j++; + if (!ISODIGIT(input[i + 1 + j])) break; + } + buf[j] = '\0'; + + if (j > 0) { + c = strtol(buf, NULL, 8); + i += 1 + j; + } + } + } + + if (c == -1) { + /* Didn't recognise encoding, copy raw bytes. */ + *d++ = input[i + 1]; + count++; + i += 2; + } else { + /* Converted the encoding. */ + *d++ = c; + count++; + } + } else { + /* Input character not a backslash, copy it. */ + *d++ = input[i++]; + count++; + } + } + + *d = '\0'; + + return count; +} + +/** + * + * IMP1 Assumes NUL-terminated + */ +int normalise_path_inplace(unsigned char *input, int input_len, int win, int *changed) { + unsigned char *src; + unsigned char *dst; + unsigned char *end; + int ldst = 0; + int hitroot = 0; + int done = 0; + int relative; + int trailing; + + *changed = 0; + + /* Need at least one byte to normalize */ + if (input_len <= 0) return 0; + + /* + * ENH: Deal with UNC and drive letters? + */ + + src = dst = input; + end = input + (input_len - 1); + ldst = 1; + + relative = ((*input == '/') || (win && (*input == '\\'))) ? 0 : 1; + trailing = ((*end == '/') || (win && (*end == '\\'))) ? 1 : 0; + + + while (!done && (src <= end) && (dst <= end)) { + /* Convert backslash to forward slash on Windows only. */ + if (win) { + if (*src == '\\') { + *src = '/'; + *changed = 1; + } + if ((src < end) && (*(src + 1) == '\\')) { + *(src + 1) = '/'; + *changed = 1; + } + } + + /* Always normalize at the end of the input. */ + if (src == end) { + done = 1; + } + + /* Skip normalization if this is NOT the end of the path segment. */ + else if (*(src + 1) != '/') { + goto copy; /* Skip normalization. */ + } + + /*** Normalize the path segment. ***/ + + /* Could it be an empty path segment? */ + if ((src != end) && *src == '/') { + /* Ignore */ + *changed = 1; + goto copy; /* Copy will take care of this. */ + } + + /* Could it be a back or self reference? */ + else if (*src == '.') { + + /* Back-reference? */ + if ((dst > input) && (*(dst - 1) == '.')) { + /* If a relative path and either our normalization has + * already hit the rootdir, or this is a backref with no + * previous path segment, then mark that the rootdir was hit + * and just copy the backref as no normilization is possible. + */ + if (relative && (hitroot || ((dst - 2) <= input))) { + hitroot = 1; + + goto copy; /* Skip normalization. */ + } + + /* Remove backreference and the previous path segment. */ + dst -= 3; + while ((dst > input) && (*dst != '/')) { + dst--; + } + + /* But do not allow going above rootdir. */ + if (dst <= input) { + hitroot = 1; + dst = input; + + /* Need to leave the root slash if this + * is not a relative path and the end was reached + * on a backreference. + */ + if (!relative && (src == end)) { + dst++; + } + } + + if (done) goto length; /* Skip the copy. */ + src++; + + *changed = 1; + } + + /* Relative Self-reference? */ + else if (dst == input) { + *changed = 1; + + /* Ignore. */ + + if (done) goto length; /* Skip the copy. */ + src++; + } + + /* Self-reference? */ + else if (*(dst - 1) == '/') { + *changed = 1; + + /* Ignore. */ + + if (done) goto length; /* Skip the copy. */ + dst--; + src++; + } + } + + /* Found a regular path segment. */ + else if (dst > input) { + hitroot = 0; + } + +copy: + /*** Copy the byte if required. ***/ + + /* Skip to the last forward slash when multiple are used. */ + if (*src == '/') { + unsigned char *oldsrc = src; + + while ( (src < end) + && ((*(src + 1) == '/') || (win && (*(src + 1) == '\\'))) ) + { + src++; + } + if (oldsrc != src) *changed = 1; + + /* Do not copy the forward slash to the root + * if it is not a relative path. Instead + * move over the slash to the next segment. + */ + if (relative && (dst == input)) { + src++; + goto length; /* Skip the copy */ + } + } + + *(dst++) = *(src++); + +length: + ldst = (dst - input); + } + + /* Make sure that there is not a trailing slash in the + * normalized form if there was not one in the original form. + */ + if (!trailing && (dst > input) && *(dst - 1) == '/') { + ldst--; + dst--; + } + + /* Always NUL terminate */ + *dst = '\0'; + + return ldst; +} + +char *modsec_build(apr_pool_t *mp) { + return apr_psprintf(mp, "%02i%02i%02i%1i%02i", + atoi(MODSEC_VERSION_MAJOR), + atoi(MODSEC_VERSION_MINOR), + atoi(MODSEC_VERSION_MAINT), + get_modsec_build_type(NULL), + atoi(MODSEC_VERSION_RELEASE)); +} + +int is_empty_string(const char *string) { + unsigned int i; + + if (string == NULL) return 1; + + for(i = 0; string[i] != '\0'; i++) { + if (!isspace(string[i])) { + return 0; + } + } + + return 1; +} + +char *resolve_relative_path(apr_pool_t *pool, const char *parent_filename, const char *filename) { + if (filename == NULL) return NULL; + // TODO Support paths on operating systems other than Unix. + if (filename[0] == '/') return (char *)filename; + + return apr_pstrcat(pool, apr_pstrndup(pool, parent_filename, + strlen(parent_filename) - strlen(apr_filepath_name_get(parent_filename))), + filename, NULL); +} + +/** + * Decode a string that contains CSS-escaped characters. + * + * References: + * http://www.w3.org/TR/REC-CSS2/syndata.html#q4 + * http://www.unicode.org/roadmaps/ + */ +int css_decode_inplace(unsigned char *input, long int input_len) { + unsigned char *d = (unsigned char *)input; + long int i, j, count; + + if (input == NULL) return -1; + + i = count = 0; + while (i < input_len) { + + /* Is the character a backslash? */ + if (input[i] == '\\') { + + /* Is there at least one more byte? */ + if (i + 1 < input_len) { + i++; /* We are not going to need the backslash. */ + + /* Check for 1-6 hex characters following the backslash */ + j = 0; + while ( (j < 6) + && (i + j < input_len) + && (VALID_HEX(input[i + j]))) + { + j++; + } + + if (j > 0) { /* We have at least one valid hexadecimal character. */ + int fullcheck = 0; + + /* For now just use the last two bytes. */ + switch (j) { + /* Number of hex characters */ + case 1: + *d++ = xsingle2c(&input[i]); + break; + + case 2: + case 3: + /* Use the last two from the end. */ + *d++ = x2c(&input[i + j - 2]); + break; + + case 4: + /* Use the last two from the end, but request + * a full width check. + */ + *d = x2c(&input[i + j - 2]); + fullcheck = 1; + break; + + case 5: + /* Use the last two from the end, but request + * a full width check if the number is greater + * or equal to 0xFFFF. + */ + *d = x2c(&input[i + j - 2]); + + /* Do full check if first byte is 0 */ + if (input[i] == '0') { + fullcheck = 1; + } + else { + d++; + } + break; + + case 6: + /* Use the last two from the end, but request + * a full width check if the number is greater + * or equal to 0xFFFF. + */ + *d = x2c(&input[i + j - 2]); + + /* Do full check if first/second bytes are 0 */ + if ( (input[i] == '0') + && (input[i + 1] == '0') + ) { + fullcheck = 1; + } + else { + d++; + } + break; + } + + /* Full width ASCII (0xff01 - 0xff5e) needs 0x20 added */ + if (fullcheck) { + if ( (*d > 0x00) && (*d < 0x5f) + && ((input[i + j - 3] == 'f') || + (input[i + j - 3] == 'F')) + && ((input[i + j - 4] == 'f') || + (input[i + j - 4] == 'F'))) + { + (*d) += 0x20; + } + + d++; + } + + /* We must ignore a single whitespace after a hex escape */ + if ((i + j < input_len) && isspace(input[i + j])) { + j++; + } + + /* Move over. */ + count++; + i += j; + } + + /* No hexadecimal digits after backslash */ + else if (input[i] == '\n') { + /* A newline character following backslash is ignored. */ + i++; + } + + /* The character after backslash is not a hexadecimal digit, nor a newline. */ + else { + /* Use one character after backslash as is. */ + *d++ = input[i++]; + count++; + } + } + + /* No characters after backslash. */ + else { + /* Do not include backslash in output (continuation to nothing) */ + i++; + } + } + + /* Character is not a backslash. */ + else { + /* Copy one normal character to output. */ + *d++ = input[i++]; + count++; + } + } + + /* Terminate output string. */ + *d = '\0'; + + return count; +} + +/** + * Translate UNIX octal umask/mode to APR apr_fileperms_t + */ +apr_fileperms_t mode2fileperms(int mode) { + apr_fileperms_t perms = 0; + + if (mode & S_IXOTH) perms |= APR_WEXECUTE; + if (mode & S_IWOTH) perms |= APR_WWRITE; + if (mode & S_IROTH) perms |= APR_WREAD; + if (mode & S_IXGRP) perms |= APR_GEXECUTE; + if (mode & S_IWGRP) perms |= APR_GWRITE; + if (mode & S_IRGRP) perms |= APR_GREAD; + if (mode & S_IXUSR) perms |= APR_UEXECUTE; + if (mode & S_IWUSR) perms |= APR_UWRITE; + if (mode & S_IRUSR) perms |= APR_UREAD; + if (mode & S_ISVTX) perms |= APR_WSTICKY; + if (mode & S_ISGID) perms |= APR_GSETID; + if (mode & S_ISUID) perms |= APR_USETID; + + return perms; +} + diff --git a/2.5.13/2.5.x/apache2/msc_util.h b/2.5.13/2.5.x/apache2/msc_util.h new file mode 100644 index 00000000..b8482dd2 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_util.h @@ -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 +#include + +#ifndef APR_WSTICKY +/* Add extra flags added to APR in 0.9.5 */ +#define APR_USETID 0x8000 /**< Set user id */ +#define APR_GSETID 0x4000 /**< Set group id */ +#define APR_WSTICKY 0x2000 /**< Sticky bit */ +#endif + +#include "modsecurity.h" + +int DSOLOCAL 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 diff --git a/2.5.13/2.5.x/apache2/msc_xml.c b/2.5.13/2.5.x/apache2/msc_xml.c new file mode 100644 index 00000000..7361f371 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_xml.c @@ -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; +} diff --git a/2.5.13/2.5.x/apache2/msc_xml.h b/2.5.13/2.5.x/apache2/msc_xml.h new file mode 100644 index 00000000..9e27fe32 --- /dev/null +++ b/2.5.13/2.5.x/apache2/msc_xml.h @@ -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 +#include + +/* Structures */ + +struct xml_data { + xmlSAXHandler *sax_handler; + xmlParserCtxtPtr parsing_ctx; + xmlDocPtr doc; + + unsigned int well_formed; +}; + +/* Functions */ + +int DSOLOCAL xml_init(modsec_rec *msr, char **error_msg); + +int DSOLOCAL xml_process_chunk(modsec_rec *msr, const char *buf, + unsigned int size, char **error_msg); + +int DSOLOCAL xml_complete(modsec_rec *msr, char **error_msg); + +apr_status_t DSOLOCAL xml_cleanup(modsec_rec *msr); + +#endif diff --git a/2.5.13/2.5.x/apache2/pdf_protect.c b/2.5.13/2.5.x/apache2/pdf_protect.c new file mode 100644 index 00000000..233fe60f --- /dev/null +++ b/2.5.13/2.5.x/apache2/pdf_protect.c @@ -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 +#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; +} diff --git a/2.5.13/2.5.x/apache2/pdf_protect.h b/2.5.13/2.5.x/apache2/pdf_protect.h new file mode 100644 index 00000000..dddc20b8 --- /dev/null +++ b/2.5.13/2.5.x/apache2/pdf_protect.h @@ -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 diff --git a/2.5.13/2.5.x/apache2/persist_dbm.c b/2.5.13/2.5.x/apache2/persist_dbm.c new file mode 100644 index 00000000..70a0730f --- /dev/null +++ b/2.5.13/2.5.x/apache2/persist_dbm.c @@ -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; +} diff --git a/2.5.13/2.5.x/apache2/persist_dbm.h b/2.5.13/2.5.x/apache2/persist_dbm.h new file mode 100644 index 00000000..b94b58da --- /dev/null +++ b/2.5.13/2.5.x/apache2/persist_dbm.h @@ -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 diff --git a/2.5.13/2.5.x/apache2/re.c b/2.5.13/2.5.x/apache2/re.c new file mode 100644 index 00000000..bdc5a8cf --- /dev/null +++ b/2.5.13/2.5.x/apache2/re.c @@ -0,0 +1,2509 @@ +/* + * 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 + +#include "re.h" + +#if defined(WITH_LUA) +#include "msc_lua.h" +#endif + +static const char *const severities[] = { + "EMERGENCY", + "ALERT", + "CRITICAL", + "ERROR", + "WARNING", + "NOTICE", + "INFO", + "DEBUG", + NULL, +}; + +/* -- Actions, variables, functions and operator functions ----------------- */ + +/** + * Remove actions with the same cardinality group from the actionset. + */ +static void msre_actionset_cardinality_fixup(msre_actionset *actionset, msre_action *action) { + const apr_array_header_t *tarr = NULL; + const apr_table_entry_t *telts = NULL; + int i; + + if ((actionset == NULL) || (action == NULL)) return; + + tarr = apr_table_elts(actionset->actions); + telts = (const apr_table_entry_t*)tarr->elts; + + for (i = 0; i < tarr->nelts; i++) { + msre_action *target = (msre_action *)telts[i].val; + if (target->metadata->cardinality_group == action->metadata->cardinality_group) { + + apr_table_unset(actionset->actions, target->metadata->name); + } + } +} + +/** + * Generate an action string from an actionset. + */ +char *msre_actionset_generate_action_string(apr_pool_t *pool, const msre_actionset *actionset) +{ + const apr_array_header_t *tarr = NULL; + const apr_table_entry_t *telts = NULL; + char *actions = NULL; + int chain; + int i; + + if (actionset == NULL) return NULL; + + chain = ((actionset->rule != NOT_SET_P) && actionset->rule->chain_starter) ? 1 : 0; + + tarr = apr_table_elts(actionset->actions); + telts = (const apr_table_entry_t*)tarr->elts; + + for (i = 0; i < tarr->nelts; i++) { + msre_action *action = (msre_action *)telts[i].val; + int use_quotes = 0; + + if (chain) { + /* Skip some actions that are not used in a chain. */ + if ( (action->metadata->type == ACTION_DISRUPTIVE) + || (action->metadata->type == ACTION_METADATA) + || (strcmp("log", action->metadata->name) == 0) + || (strcmp("auditlog", action->metadata->name) == 0) + || (strcmp("nolog", action->metadata->name) == 0) + || (strcmp("noauditlog", action->metadata->name) == 0) + || (strcmp("severity", action->metadata->name) == 0) + || (strcmp("tag", action->metadata->name) == 0) + || (strcmp("phase", action->metadata->name) == 0)) + { + continue; + } + } + + /* Check if we need any quotes */ + if (action->param != NULL) { + int j; + for(j = 0; action->param[j] != '\0'; j++) { + if (isspace(action->param[j])) { + use_quotes = 1; + break; + } + } + if (j == 0) use_quotes = 1; + } + + actions = apr_pstrcat(pool, + (actions == NULL) ? "" : actions, + (actions == NULL) ? "" : ",", + action->metadata->name, + (action->param == NULL) ? "" : ":", + (use_quotes) ? "'" : "", + (action->param == NULL) ? "" : action->param, + (use_quotes) ? "'" : "", + NULL); + } + + return actions; +} + +/** + * Add an action to an actionset. + */ +static void msre_actionset_action_add(msre_actionset *actionset, msre_action *action) +{ + msre_action *add_action = action; + + if ((actionset == NULL)) return; + + /** + * The "block" action is just a placeholder for the parent action. + */ + if ((actionset->parent_intercept_action_rec != NULL) && (actionset->parent_intercept_action_rec != NOT_SET_P) && (strcmp("block", action->metadata->name) == 0) && (strcmp("block", action->metadata->name) == 0)) { + /* revert back to parent */ + actionset->intercept_action = actionset->parent_intercept_action; + add_action = actionset->parent_intercept_action_rec; + } + + if ((add_action == NULL)) return; + + if (add_action->metadata->cardinality_group != ACTION_CGROUP_NONE) { + msre_actionset_cardinality_fixup(actionset, add_action); + } + + if (add_action->metadata->cardinality == ACTION_CARDINALITY_ONE) { + /* One action per actionlist. */ + apr_table_setn(actionset->actions, add_action->metadata->name, (void *)add_action); + } else { + /* Multiple actions per actionlist. */ + apr_table_addn(actionset->actions, add_action->metadata->name, (void *)add_action); + } +} + +/** + * Creates msre_var instances (rule variables) out of the + * given text string and places them into the supplied table. + */ +apr_status_t msre_parse_targets(msre_ruleset *ruleset, const char *text, + apr_array_header_t *arr, char **error_msg) +{ + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + apr_table_t *vartable; + unsigned int count = 0; + apr_status_t rc; + msre_var *var; + int i; + + if (text == NULL) return -1; + + /* Extract name & value pairs first */ + vartable = apr_table_make(ruleset->mp, 10); + if (vartable == NULL) return -1; + rc = msre_parse_generic(ruleset->mp, text, vartable, error_msg); + if (rc < 0) return rc; + + /* Loop through the table and create variables */ + tarr = apr_table_elts(vartable); + telts = (const apr_table_entry_t*)tarr->elts; + for (i = 0; i < tarr->nelts; i++) { + var = msre_create_var(ruleset, telts[i].key, telts[i].val, NULL, error_msg); + if (var == NULL) return -1; + *(msre_var **)apr_array_push(arr) = var; + count++; + } + + return count; +} + +/** + * Creates msre_action instances by parsing the given string, placing + * them into the supplied array. + */ +apr_status_t msre_parse_actions(msre_engine *engine, msre_actionset *actionset, + const char *text, char **error_msg) +{ + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + apr_table_t *vartable; + unsigned int count = 0; + apr_status_t rc; + msre_action *action; + int i; + + if (text == NULL) return -1; + + /* Extract name & value pairs first */ + vartable = apr_table_make(engine->mp, 10); + if (vartable == NULL) return -1; + rc = msre_parse_generic(engine->mp, text, vartable, error_msg); + if (rc < 0) return rc; + + /* Loop through the table and create actions */ + tarr = apr_table_elts(vartable); + telts = (const apr_table_entry_t*)tarr->elts; + for (i = 0; i < tarr->nelts; i++) { + /* Create action. */ + action = msre_create_action(engine, telts[i].key, telts[i].val, error_msg); + if (action == NULL) return -1; + + /* Initialise action (option). */ + if (action->metadata->init != NULL) { + action->metadata->init(engine, actionset, action); + } + + msre_actionset_action_add(actionset, action); + + count++; + } + + return count; +} + +/** + * Locates variable metadata given the variable name. + */ +msre_var_metadata *msre_resolve_var(msre_engine *engine, const char *name) { + return (msre_var_metadata *)apr_table_get(engine->variables, name); +} + +/** + * Locates action metadata given the action name. + */ +msre_action_metadata *msre_resolve_action(msre_engine *engine, const char *name) { + return (msre_action_metadata *)apr_table_get(engine->actions, name); +} + +/** + * Creates a new variable instance given the variable name + * and an (optional) parameter. + */ +msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char *name, const char *param, + modsec_rec *msr, char **error_msg) +{ + const char *varparam = param; + msre_var *var = apr_pcalloc(pool, sizeof(msre_var)); + if (var == NULL) return NULL; + + if (error_msg == NULL) return NULL; + *error_msg = NULL; + + /* Handle negation and member counting */ + if (name[0] == '!') { + var->is_negated = 1; + var->name = name + 1; + } + else + if (name[0] == '&') { + var->is_counting = 1; + var->name = name + 1; + } + else { + var->name = name; + } + + /* Treat HTTP_* targets as an alias for REQUEST_HEADERS:* */ + if ( (var->name != NULL) + && (strlen(var->name) > 5) + && (strncmp("HTTP_", var->name, 5) == 0)) + { + const char *oldname = var->name; + var->name = apr_pstrdup(pool, "REQUEST_HEADERS"); + varparam = apr_pstrdup(pool, oldname + 5); + } + + + /* Resolve variable */ + var->metadata = msre_resolve_var(engine, var->name); + if (var->metadata == NULL) { + *error_msg = apr_psprintf(engine->mp, "Unknown variable: %s", name); + return NULL; + } + + /* The counting operator "&" can only be used against collections. */ + if (var->is_counting) { + if (var->metadata->type == VAR_SIMPLE) { + *error_msg = apr_psprintf(engine->mp, "The & modificator does not apply to " + "non-collection variables."); + return NULL; + } + } + + /* Check the parameter. */ + if (varparam == NULL) { + if (var->metadata->argc_min > 0) { + *error_msg = apr_psprintf(engine->mp, "Missing mandatory parameter for variable %s.", + name); + return NULL; + } + } else { /* Parameter present */ + + /* Do we allow a parameter? */ + if (var->metadata->argc_max == 0) { + *error_msg = apr_psprintf(engine->mp, "Variable %s does not support parameters.", + name); + return NULL; + } + + var->param = varparam; + } + + return var; +} + +/** + * Create a new variable object from the provided name and value. + * + * NOTE: this allocates out of the global pool and should not be used + * per-request + */ +msre_var *msre_create_var(msre_ruleset *ruleset, const char *name, const char *param, + modsec_rec *msr, char **error_msg) +{ + msre_var *var = msre_create_var_ex(ruleset->engine->mp, ruleset->engine, name, param, msr, error_msg); + if (var == NULL) return NULL; + + /* Validate & initialise variable */ + if (var->metadata->validate != NULL) { + *error_msg = var->metadata->validate(ruleset, var); + if (*error_msg != NULL) { + return NULL; + } + } + + return var; +} + +/** + * Creates a new action instance given its name and an (optional) parameter. + */ +msre_action *msre_create_action(msre_engine *engine, const char *name, const char *param, + char **error_msg) +{ + msre_action *action = apr_pcalloc(engine->mp, sizeof(msre_action)); + if (action == NULL) return NULL; + + if (error_msg == NULL) return NULL; + *error_msg = NULL; + + /* Resolve action */ + action->metadata = msre_resolve_action(engine, name); + if (action->metadata == NULL) { + *error_msg = apr_psprintf(engine->mp, "Unknown action: %s", name); + return NULL; + } + + if (param == NULL) { /* Parameter not present */ + if (action->metadata->argc_min > 0) { + *error_msg = apr_psprintf(engine->mp, "Missing mandatory parameter for action %s", + name); + return NULL; + } + } else { /* Parameter present */ + + /* Should we allow the parameter? */ + if (action->metadata->argc_max == 0) { + *error_msg = apr_psprintf(engine->mp, "Extra parameter provided to action %s", name); + return NULL; + } + + /* Handle +/- modificators */ + if ((param[0] == '+')||(param[0] == '-')) { + if (action->metadata->allow_param_plusminus == 0) { + *error_msg = apr_psprintf(engine->mp, + "Action %s does not allow +/- modificators.", name); + return NULL; + } + else { /* Modificators allowed. */ + if (param[0] == '+') { + action->param = param + 1; + action->param_plusminus = POSITIVE_VALUE; + } else + if (param[0] == '-') { + action->param = param + 1; + action->param_plusminus = NEGATIVE_VALUE; + } + } + } else { + action->param = param; + } + + /* Validate parameter */ + if (action->metadata->validate != NULL) { + *error_msg = action->metadata->validate(engine, action); + if (*error_msg != NULL) return NULL; + } + } + + return action; +} + +/** + * Generic parser that is used as basis for target and action parsing. + * It breaks up the input string into name-parameter pairs and places + * them into the given table. + */ +int msre_parse_generic(apr_pool_t *mp, const char *text, apr_table_t *vartable, + char **error_msg) +{ + char *p = (char *)text; + int count = 0; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + count = 0; + while(*p != '\0') { + char *name = NULL, *value = NULL; + + /* ignore whitespace */ + while(isspace(*p)) p++; + if (*p == '\0') return count; + + /* we are at the beginning of the name */ + name = p; + while((*p != '\0')&&(*p != '|')&&(*p != ':')&&(*p != ',')&&(!isspace(*p))) p++; /* ENH replace with isvarnamechar() */ + + /* get the name */ + name = apr_pstrmemdup(mp, name, p - name); + + if (*p != ':') { /* we don't have a parameter */ + /* add to the table with no value */ + apr_table_addn(vartable, name, NULL); + count++; + + /* go over any whitespace present */ + while(isspace(*p)) p++; + + /* we're done */ + if (*p == '\0') { + return count; + } + + /* skip over the separator character and continue */ + if ((*p == ',')||(*p == '|')) { + p++; + continue; + } + + *error_msg = apr_psprintf(mp, "Unexpected character at position %d: %s", + (int)(p - text), text); + return -1; + } + + /* we have a parameter */ + + p++; /* move over the colon */ + + /* we'll allow empty values */ + if (*p == '\0') { + apr_table_addn(vartable, name, NULL); + count++; + return count; + } + + if ((*p == ',')||(*p == '|')) { + apr_table_addn(vartable, name, NULL); + count++; + /* move over the separator char and continue */ + p++; + continue; + } + + /* we really have a parameter */ + + if (*p == '\'') { /* quoted value */ + char *d = NULL; + + p++; /* go over the openning quote */ + value = d = strdup(p); + if (d == NULL) return -1; + + for(;;) { + if (*p == '\0') { + *error_msg = apr_psprintf(mp, "Missing closing quote at position %d: %s", + (int)(p - text), text); + free(value); + return -1; + } else + if (*p == '\\') { + if ( (*(p + 1) == '\0') || ((*(p + 1) != '\'')&&(*(p + 1) != '\\')) ) { + *error_msg = apr_psprintf(mp, "Invalid quoted pair at position %d: %s", + (int)(p - text), text); + free(value); + return -1; + } + p++; + *(d++) = *(p++); + } else + if (*p == '\'') { + *d = '\0'; + p++; + break; + } + else { + *(d++) = *(p++); + } + } + + d = value; + value = apr_pstrdup(mp, d); + free(d); + } else { /* non-quoted value */ + value = p; + while((*p != '\0')&&(*p != ',')&&(*p != '|')&&(!isspace(*p))) p++; + value = apr_pstrmemdup(mp, value, p - value); + } + + /* add to table */ + apr_table_addn(vartable, name, value); + count++; + + /* move to the first character of the next name-value pair */ + while(isspace(*p)||(*p == ',')||(*p == '|')) p++; + } + + return count; +} + + +/* -- Actionset functions -------------------------------------------------- */ + +/** + * Creates an actionset instance and (as an option) populates it by + * parsing the given string which contains a list of actions. + */ +msre_actionset *msre_actionset_create(msre_engine *engine, const char *text, + char **error_msg) +{ + msre_actionset *actionset = (msre_actionset *)apr_pcalloc(engine->mp, + sizeof(msre_actionset)); + if (actionset == NULL) return NULL; + + actionset->actions = apr_table_make(engine->mp, 25); + if (actionset->actions == NULL) return NULL; + + /* Metadata */ + actionset->id = NOT_SET_P; + actionset->rev = NOT_SET_P; + actionset->msg = NOT_SET_P; + actionset->logdata = NOT_SET_P; + actionset->phase = NOT_SET; + actionset->severity = -1; + actionset->rule = NOT_SET_P; + + /* Flow */ + actionset->is_chained = NOT_SET; + actionset->skip_count = NOT_SET; + actionset->skip_after = NOT_SET_P; + + /* Disruptive */ + actionset->parent_intercept_action_rec = NOT_SET_P; + actionset->intercept_action_rec = NOT_SET_P; + actionset->parent_intercept_action = NOT_SET; + actionset->intercept_action = NOT_SET; + actionset->intercept_uri = NOT_SET_P; + actionset->intercept_status = NOT_SET; + actionset->intercept_pause = NOT_SET; + + /* Other */ + actionset->auditlog = NOT_SET; + actionset->log = NOT_SET; + + /* Parse the list of actions, if it's present */ + if (text != NULL) { + if (msre_parse_actions(engine, actionset, text, error_msg) < 0) { + return NULL; + } + } + + return actionset; +} + +/** + * Create a (shallow) copy of the supplied actionset. + */ +static msre_actionset *msre_actionset_copy(apr_pool_t *mp, msre_actionset *orig) { + msre_actionset *copy = NULL; + + if (orig == NULL) return NULL; + copy = (msre_actionset *)apr_pmemdup(mp, orig, sizeof(msre_actionset)); + if (copy == NULL) return NULL; + copy->actions = apr_table_copy(mp, orig->actions); + + return copy; +} + +/** + * Merges two actionsets into one. + */ +msre_actionset *msre_actionset_merge(msre_engine *engine, msre_actionset *parent, + msre_actionset *child, int inherit_by_default) +{ + msre_actionset *merged = NULL; + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + int i; + + if (inherit_by_default == 0) { + /* There is nothing to merge in this case. */ + return msre_actionset_copy(engine->mp, child); + } + + /* Start with a copy of the parent configuration. */ + merged = msre_actionset_copy(engine->mp, parent); + if (merged == NULL) return NULL; + + if (child == NULL) { + /* The child actionset does not exist, hence + * go with the parent one. + */ + return merged; + } + + /* First merge the hard-coded stuff. */ + + /* Metadata */ + if (child->id != NOT_SET_P) merged->id = child->id; + if (child->rev != NOT_SET_P) merged->rev = child->rev; + if (child->msg != NOT_SET_P) merged->msg = child->msg; + if (child->logdata != NOT_SET_P) merged->logdata = child->logdata; + if (child->severity != NOT_SET) merged->severity = child->severity; + if (child->phase != NOT_SET) merged->phase = child->phase; + if (child->rule != NOT_SET_P) merged->rule = child->rule; + + /* Flow */ + merged->is_chained = child->is_chained; + if (child->skip_count != NOT_SET) merged->skip_count = child->skip_count; + if (child->skip_after != NOT_SET_P) merged->skip_after = child->skip_after; + + /* Disruptive */ + if (child->intercept_action != NOT_SET) { + merged->intercept_action_rec = child->intercept_action_rec; + merged->intercept_action = child->intercept_action; + merged->intercept_uri = child->intercept_uri; + } + + if (child->intercept_status != NOT_SET) merged->intercept_status = child->intercept_status; + if (child->intercept_pause != NOT_SET) merged->intercept_pause = child->intercept_pause; + + /* Other */ + if (child->auditlog != NOT_SET) merged->auditlog = child->auditlog; + if (child->log != NOT_SET) merged->log = child->log; + + + /* Now merge the actions. */ + + tarr = apr_table_elts(child->actions); + telts = (const apr_table_entry_t*)tarr->elts; + for (i = 0; i < tarr->nelts; i++) { + msre_actionset_action_add(merged, (msre_action *)telts[i].val); + } + + return merged; +} + +/** + * Creates an actionset that contains a default list of actions. + */ +msre_actionset *msre_actionset_create_default(msre_engine *engine) { + char *my_error_msg = NULL; + return msre_actionset_create(engine, + "phase:2,log,auditlog,pass", + &my_error_msg); +} + +/** + * Sets the default values for the hard-coded actionset configuration. + */ +void msre_actionset_set_defaults(msre_actionset *actionset) { + /* Metadata */ + if (actionset->id == NOT_SET_P) actionset->id = NULL; + if (actionset->rev == NOT_SET_P) actionset->rev = NULL; + if (actionset->msg == NOT_SET_P) actionset->msg = NULL; + if (actionset->logdata == NOT_SET_P) actionset->logdata = NULL; + if (actionset->phase == NOT_SET) actionset->phase = 2; + if (actionset->severity == -1) {} /* leave at -1 */ + if (actionset->rule == NOT_SET_P) actionset->rule = NULL; + + /* Flow */ + if (actionset->is_chained == NOT_SET) actionset->is_chained = 0; + if (actionset->skip_count == NOT_SET) actionset->skip_count = 0; + if (actionset->skip_after == NOT_SET_P) actionset->skip_after = NULL; + + /* Disruptive */ + if (actionset->parent_intercept_action_rec == NOT_SET_P) actionset->parent_intercept_action_rec = NULL; + if (actionset->intercept_action_rec == NOT_SET_P) actionset->intercept_action_rec = NULL; + if (actionset->parent_intercept_action == NOT_SET) actionset->parent_intercept_action = ACTION_NONE; + if (actionset->intercept_action == NOT_SET) actionset->intercept_action = ACTION_NONE; + if (actionset->intercept_uri == NOT_SET_P) actionset->intercept_uri = NULL; + if (actionset->intercept_status == NOT_SET) actionset->intercept_status = 403; + if (actionset->intercept_pause == NOT_SET) actionset->intercept_pause = 0; + + /* Other */ + if (actionset->auditlog == NOT_SET) actionset->auditlog = 1; + if (actionset->log == NOT_SET) actionset->log = 1; +} + +/* -- Engine functions ----------------------------------------------------- */ + +/** + * Creates a new engine instance. + */ +msre_engine *msre_engine_create(apr_pool_t *parent_pool) { + msre_engine *engine; + apr_pool_t *mp; + + /* Create new memory pool */ + if (apr_pool_create(&mp, parent_pool) != APR_SUCCESS) return NULL; + + /* Init fields */ + engine = apr_pcalloc(mp, sizeof(msre_engine)); + if (engine == NULL) return NULL; + engine->mp = mp; + engine->tfns = apr_table_make(mp, 25); + if (engine->tfns == NULL) return NULL; + engine->operators = apr_table_make(mp, 25); + if (engine->operators == NULL) return NULL; + engine->variables = apr_table_make(mp, 25); + if (engine->variables == NULL) return NULL; + engine->actions = apr_table_make(mp, 25); + if (engine->actions == NULL) return NULL; + + return engine; +} + +/** + * Destroys an engine instance, releasing the consumed memory. + */ +void msre_engine_destroy(msre_engine *engine) { + /* Destroyed automatically by the parent pool. + * apr_pool_destroy(engine->mp); + */ +} + + +/* -- Recipe functions ----------------------------------------------------- */ + +#define NEXT_CHAIN 1 +#define NEXT_RULE 2 +#define SKIP_RULES 3 + + + +/** + * Default implementation of the ruleset phase processing; it processes + * the rules in the ruleset attached to the currently active + * transaction phase. + */ +#if defined(PERFORMANCE_MEASUREMENT) + +#define PERFORMANCE_MEASUREMENT_LOOP 1000 + +static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_rec *msr); + +apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) { + apr_array_header_t *arr = NULL; + msre_rule **rules = NULL; + apr_status_t rc; + int i; + + switch (msr->phase) { + case PHASE_REQUEST_HEADERS : + arr = ruleset->phase_request_headers; + break; + case PHASE_REQUEST_BODY : + arr = ruleset->phase_request_body; + break; + case PHASE_RESPONSE_HEADERS : + arr = ruleset->phase_response_headers; + break; + case PHASE_RESPONSE_BODY : + arr = ruleset->phase_response_body; + break; + case PHASE_LOGGING : + arr = ruleset->phase_logging; + break; + default : + msr_log(msr, 1, "Internal Error: Invalid phase %d", msr->phase); + return -1; + } + + rules = (msre_rule **)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msre_rule *rule = rules[i]; + rule->execution_time = 0; + } + + for (i = 0; i < PERFORMANCE_MEASUREMENT_LOOP; i++) { + rc = msre_ruleset_process_phase_(ruleset, msr); + } + + msr_log(msr, 1, "Phase %d", msr->phase); + + rules = (msre_rule **)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msre_rule *rule = rules[i]; + + /* Ignore markers, which are never processed. */ + if (rule->placeholder == RULE_PH_MARKER) continue; + + msr_log(msr, 1, "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"]: %u usec", rule, + ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) ? rule->actionset->id : "-", + rule->filename != NULL ? rule->filename : "-", + rule->line_num, + (rule->execution_time / PERFORMANCE_MEASUREMENT_LOOP)); + } + + return rc; +} + +static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_rec *msr) { +#else +apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) { +#endif + apr_array_header_t *arr = NULL; + msre_rule **rules; + apr_status_t rc; + const char *skip_after = NULL; + msre_rule *last_rule = NULL; + int i, mode, skip, skipped, saw_starter; + + /* First determine which set of rules we need to use. */ + switch (msr->phase) { + case PHASE_REQUEST_HEADERS : + arr = ruleset->phase_request_headers; + break; + case PHASE_REQUEST_BODY : + arr = ruleset->phase_request_body; + break; + case PHASE_RESPONSE_HEADERS : + arr = ruleset->phase_response_headers; + break; + case PHASE_RESPONSE_BODY : + arr = ruleset->phase_response_body; + break; + case PHASE_LOGGING : + arr = ruleset->phase_logging; + break; + default : + msr_log(msr, 1, "Internal Error: Invalid phase %d", msr->phase); + return -1; + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "This phase consists of %d rule(s).", arr->nelts); + } + + /* Loop through the rules in the selected set. */ + skip = 0; + skipped = 0; + saw_starter = 0; + mode = NEXT_RULE; + rules = (msre_rule **)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msre_rule *rule = rules[i]; + #if defined(PERFORMANCE_MEASUREMENT) + apr_time_t time1 = 0; + #endif + + /* Reset the rule interception flag */ + msr->rule_was_intercepted = 0; + + /* SKIP_RULES is used to skip all rules until we hit a placeholder + * with the specified rule ID and then resume execution after that. + */ + + if (mode == SKIP_RULES) { + /* Go to the next rule if we have not yet hit the skip_after ID */ + if ((rule->placeholder == RULE_PH_NONE) || (rule->actionset->id == NULL) || (strcmp(skip_after, rule->actionset->id) != 0)) { + + if((i-1) >= 0) + last_rule = rules[i-1]; + else + last_rule = rules[0]; + + if((last_rule != NULL) && (last_rule->actionset != NULL ) && last_rule->actionset->is_chained && (saw_starter == 1)) { + mode = NEXT_RULE; + skipped = 1; + --i; + } else { + mode = SKIP_RULES; + skipped = 0; + saw_starter = 0; + + if (msr->txcfg->debuglog_level >= 9) { + if(last_rule != NULL) + msr_log(msr, 9, "Current rule is id=\"%s\" [chained %d] is trying to find the SecMarker=\"%s\" [stater %d]",rule->actionset->id,last_rule->actionset->is_chained,skip_after,saw_starter); + } + + } + + continue; + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Found rule %pp id=\"%s\".", rule, skip_after); + } + + /* Go to the rule *after* this one to continue execution. */ + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Continuing execution after rule id=\"%s\".", skip_after); + } + + saw_starter = 0; + skipped = 0; + skip_after = NULL; + mode = NEXT_RULE; + continue; + } + + /* Skip any rule marked as a placeholder */ + if (rule->placeholder != RULE_PH_NONE) { + continue; + } + + /* NEXT_CHAIN is used when one of the rules in a chain + * fails to match and then we need to skip the remaining + * rules in that chain in order to get to the next + * rule that can execute. + */ + if (mode == NEXT_CHAIN) { + if (rule->actionset->is_chained == 0) { + mode = NEXT_RULE; + } + + /* Go to the next rule. */ + continue; + } + + /* If we are here that means the mode is NEXT_RULE, which + * then means we have done processing any chains. However, + * if the "skip" parameter is set we need to skip over. + */ + if ((mode == NEXT_RULE)&&(skip > 0)) { + /* Decrement the skip counter by one. */ + skip--; + + /* If the current rule is part of a chain then + * we need to skip over the entire chain. Thus + * we change the mode to NEXT_CHAIN. The skip + * counter will not decrement as we are moving + * over the rules belonging to the chain. + */ + if (rule->actionset->is_chained) { + mode = NEXT_CHAIN; + } + + /* Go to the next rule. */ + continue; + } + + /* Check if this rule was removed at runtime */ + if ((rule->actionset->id !=NULL) && (! apr_is_empty_array(msr->removed_rules))) { + int j; + int do_process = 1; + const char *range; + + for(j = 0; j < msr->removed_rules->nelts; j++) { + range = ((const char**)msr->removed_rules->elts)[j]; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Checking removal of rule id=\"%s\" against: %s", rule->actionset->id, range); + } + + if (rule_id_in_range(atoi(rule->actionset->id), range)) { + do_process = 0; + break; + } + } + + /* Go to the next rule if this one has been removed. */ + if (do_process == 0) { + if (msr->txcfg->debuglog_level >= 5) { + msr_log(msr, 5, "Not processing %srule id=\"%s\": " + "removed by ctl action", + rule->actionset->is_chained ? "chained " : "", + rule->actionset->id); + } + + /* Skip the whole chain, if this is a chained rule */ + if (rule->actionset->is_chained) { + mode = NEXT_CHAIN; + } + + skipped = 0; + saw_starter = 0; + continue; + } + } + + if (msr->txcfg->debuglog_level >= 4) { + apr_pool_t *p = msr->mp; + const char *fn = NULL; + const char *id = NULL; + const char *rev = NULL; + + if (rule->filename != NULL) { + fn = apr_psprintf(p, " [file \"%s\"] [line \"%d\"]", rule->filename, rule->line_num); + } + + if (rule->actionset != NULL && rule->actionset->id != NULL) { + id = apr_psprintf(p, " [id \"%s\"]", rule->actionset->id); + } + + if (rule->actionset != NULL && rule->actionset->rev != NULL) { + rev = apr_psprintf(p, " [rev \"%s\"]", rule->actionset->rev); + } + + msr_log(msr, 4, "Recipe: Invoking rule %pp;%s%s%s.", + rule, (fn ? fn : ""), (id ? id : ""), (rev ? rev : "")); + msr_log(msr, 5, "Rule %pp: %s", rule, rule->unparsed); + } + + #if defined(PERFORMANCE_MEASUREMENT) + time1 = apr_time_now(); + #endif + + rc = msre_rule_process(rule, msr); + + #if defined(PERFORMANCE_MEASUREMENT) + rule->execution_time += (apr_time_now() - time1); + #endif + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Rule returned %d.", rc); + } + + if (rc == RULE_NO_MATCH) { + if (rule->actionset->is_chained) { + /* If the current rule is part of a chain then + * we need to skip over all the rules in the chain. + */ + mode = NEXT_CHAIN; + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "No match, chained -> mode NEXT_CHAIN."); + } + } else { + /* This rule is not part of a chain so we simply + * move to the next rule. + */ + mode = NEXT_RULE; + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "No match, not chained -> mode NEXT_RULE."); + } + } + + skipped = 0; + saw_starter = 0; + } + else if (rc == RULE_MATCH) { + if (msr->rule_was_intercepted) { + /* If the transaction was intercepted by this rule we will + * go back. Do note that we are relying on the + * rule to know if it is a part of a chain and + * not intercept if it is. + */ + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Match, intercepted -> returning."); + } + + if((i-1) >= 0) + last_rule = rules[i-1]; + else + last_rule = rules[0]; + + if((last_rule != NULL) && (last_rule->actionset != NULL) && last_rule->actionset->is_chained) { + + int st = 0; + + for(st=i;st>=0;st--) { + + msre_rule *rule_starter = rules[st]; + + if((rule_starter != NULL) && (rule_starter->chain_starter != NULL)) { + if((msr != NULL) && (msr->intercept_actionset != NULL) && (rule_starter->actionset != NULL)) + msr->intercept_actionset->intercept_uri = rule_starter->actionset->intercept_uri; + break; + } + } + + } + + return 1; + } + + if (rule->actionset->skip_after != NULL) { + skip_after = rule->actionset->skip_after; + mode = SKIP_RULES; + saw_starter = 1; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Skipping after rule %pp id=\"%s\" -> mode SKIP_RULES.", rule, skip_after); + } + + continue; + } + + if(skipped == 1) { + mode = SKIP_RULES; + continue; + } + + /* We had a match but the transaction was not + * intercepted. In that case we proceed with the + * next rule... + */ + mode = NEXT_RULE; + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Match -> mode NEXT_RULE."); + } + + + /* ...unless we need to skip, in which case we + * determine how many rules/chains we need to + * skip and configure the counter accordingly. + */ + if (rule->actionset->is_chained == 0) { + if (rule->chain_starter != NULL) { + if (rule->chain_starter->actionset->skip_count > 0) { + skip = rule->chain_starter->actionset->skip_count; + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Skipping %d rules/chains (from a chain).", skip); + } + } + } + else if (rule->actionset->skip_count > 0) { + skip = rule->actionset->skip_count; + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Skipping %d rules/chains.", skip); + } + } + } + } + else if (rc < 0) { + msr_log(msr, 1, "Rule processing failed."); + return -1; + } + else { + msr_log(msr, 1, "Rule processing failed with unknown return code: %d.", rc); + return -1; + } + } + + /* ENH warn if chained rules are missing. */ + + return 0; +} + +/** + * Creates a ruleset that will be handled by the default + * implementation. + */ +msre_ruleset *msre_ruleset_create(msre_engine *engine, apr_pool_t *mp) { + msre_ruleset *ruleset; + + ruleset = apr_pcalloc(mp, sizeof(msre_ruleset)); + if (ruleset == NULL) return NULL; + ruleset->mp = mp; + ruleset->engine = engine; + + ruleset->phase_request_headers = apr_array_make(ruleset->mp, 25, sizeof(const msre_rule *)); + ruleset->phase_request_body = apr_array_make(ruleset->mp, 25, sizeof(const msre_rule *)); + ruleset->phase_response_headers = apr_array_make(ruleset->mp, 25, sizeof(const msre_rule *)); + ruleset->phase_response_body = apr_array_make(ruleset->mp, 25, sizeof(const msre_rule *)); + ruleset->phase_logging = apr_array_make(ruleset->mp, 25, sizeof(const msre_rule *)); + + return ruleset; +} + +/** + * Adds one rule to the given phase of the ruleset. + */ +int msre_ruleset_rule_add(msre_ruleset *ruleset, msre_rule *rule, int phase) { + apr_array_header_t *arr = NULL; + + switch (phase) { + case PHASE_REQUEST_HEADERS : + arr = ruleset->phase_request_headers; + break; + case PHASE_REQUEST_BODY : + arr = ruleset->phase_request_body; + break; + case PHASE_RESPONSE_HEADERS : + arr = ruleset->phase_response_headers; + break; + case PHASE_RESPONSE_BODY : + arr = ruleset->phase_response_body; + break; + case PHASE_LOGGING : + arr = ruleset->phase_logging; + break; + default : + return -1; + } + + /* ENH verify the rule's use of targets is consistent with + * the phase it selected to run at. + */ + + msre_actionset_set_defaults(rule->actionset); + rule->actionset->rule = rule; + + *(const msre_rule **)apr_array_push(arr) = rule; + + return 1; +} + +static msre_rule * msre_ruleset_fetch_phase_rule(const msre_ruleset *ruleset, const char *id, + const apr_array_header_t *phase_arr) +{ + msre_rule **rules = (msre_rule **)phase_arr->elts; + int i; + + for (i = 0; i < phase_arr->nelts; i++) { + msre_rule *rule = (msre_rule *)rules[i]; + + /* Rule with an action, not a sub-rule (chain) and a matching id */ + if ( (rule->actionset != NULL) + && (!rule->actionset->is_chained || !rule->chain_starter) + && (rule->actionset->id != NULL) + && (strcmp(rule->actionset->id, id) == 0)) + { + /* Return rule that matched unless it is a placeholder */ + return (rule->placeholder == RULE_PH_NONE) ? rule : NULL; + } + } + + return NULL; +} + +/** + * Fetches rule from the ruleset all rules that match the given exception. + */ +msre_rule * msre_ruleset_fetch_rule(msre_ruleset *ruleset, const char *id) { + msre_rule *rule = NULL; + + if (ruleset == NULL) return NULL; + + rule = msre_ruleset_fetch_phase_rule(ruleset, id, ruleset->phase_request_headers); + if (rule != NULL) return rule; + + rule = msre_ruleset_fetch_phase_rule(ruleset, id, ruleset->phase_request_body); + if (rule != NULL) return rule; + + rule = msre_ruleset_fetch_phase_rule(ruleset, id, ruleset->phase_response_headers); + if (rule != NULL) return rule; + + rule = msre_ruleset_fetch_phase_rule(ruleset, id, ruleset->phase_response_body); + if (rule != NULL) return rule; + + rule = msre_ruleset_fetch_phase_rule(ruleset, id, ruleset->phase_logging); + + return rule; +} + +static int msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset, rule_exception *re, + apr_array_header_t *phase_arr) +{ + msre_rule **rules; + int i, j, mode, removed_count; + + j = 0; + mode = 0; + removed_count = 0; + rules = (msre_rule **)phase_arr->elts; + for (i = 0; i < phase_arr->nelts; i++) { + msre_rule *rule = (msre_rule *)rules[i]; + + if (mode == 0) { /* Looking for next rule. */ + int remove_rule = 0; + + /* Only remove non-placeholder rules */ + if (rule->placeholder == RULE_PH_NONE) { + switch(re->type) { + case RULE_EXCEPTION_REMOVE_ID : + if ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) { + int ruleid = atoi(rule->actionset->id); + + if (rule_id_in_range(ruleid, re->param)) { + remove_rule = 1; + } + } + + break; + + case RULE_EXCEPTION_REMOVE_MSG : + if ((rule->actionset != NULL)&&(rule->actionset->msg != NULL)) { + char *my_error_msg = NULL; + + int rc = msc_regexec(re->param_data, + rule->actionset->msg, strlen(rule->actionset->msg), + &my_error_msg); + if (rc >= 0) { + remove_rule = 1; + } + } + + break; + } + } + + if (remove_rule) { + /* Do not increment j. */ + removed_count++; + if (rule->actionset->is_chained) mode = 2; /* Remove rules in this chain. */ + } else { + if (rule->actionset->is_chained) mode = 1; /* Keep rules in this chain. */ + rules[j++] = rules[i]; + } + } else { /* Handling rule that is part of a chain. */ + if (mode == 2) { /* We want to remove the rule. */ + /* Do not increment j. */ + removed_count++; + } else { + rules[j++] = rules[i]; + } + + if ((rule->actionset == NULL)||(rule->actionset->is_chained == 0)) mode = 0; + } + } + + /* Update the number of rules in the array. */ + phase_arr->nelts -= removed_count; + + return 0; +} + +/** + * Removes from the ruleset all rules that match the given exception. + */ +int msre_ruleset_rule_remove_with_exception(msre_ruleset *ruleset, rule_exception *re) { + int count = 0; + + if (ruleset == NULL) return 0; + + count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_request_headers); + count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_request_body); + count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_response_headers); + count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_response_body); + count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_logging); + + return count; +} + + +/* -- Rule functions ------------------------------------------------------- */ + +/** + * Returns the name of the supplied severity level. + */ +static const char *msre_format_severity(int severity) { + if ((severity >= 0)&&(severity <= 7)) { + return severities[severity]; + } + else { + return "(invalid value)"; + } +} + +/** + * Creates a string containing the metadata of the supplied rule. + */ +char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + char *id = ""; + char *rev = ""; + char *msg = ""; + char *logdata = ""; + char *severity = ""; + char *tags = ""; + char *fn = ""; + int k; + + if (actionset == NULL) return ""; + + if ((actionset->rule != NULL) && (actionset->rule->filename != NULL)) { + fn = apr_psprintf(msr->mp, " [file \"%s\"] [line \"%d\"]", + actionset->rule->filename, actionset->rule->line_num); + } + if (actionset->id != NULL) { + id = apr_psprintf(msr->mp, " [id \"%s\"]", + log_escape(msr->mp, actionset->id)); + } + if (actionset->rev != NULL) { + rev = apr_psprintf(msr->mp, " [rev \"%s\"]", + log_escape(msr->mp, actionset->rev)); + } + if (actionset->msg != NULL) { + /* Expand variables in the message string. */ + msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + var->value = (char *)actionset->msg; + var->value_len = strlen(actionset->msg); + expand_macros(msr, var, NULL, msr->mp); + + msg = apr_psprintf(msr->mp, " [msg \"%s\"]", + log_escape_ex(msr->mp, var->value, var->value_len)); + } + if (actionset->logdata != NULL) { + /* Expand variables in the message string. */ + msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + var->value = (char *)actionset->logdata; + var->value_len = strlen(actionset->logdata); + expand_macros(msr, var, NULL, msr->mp); + + logdata = apr_psprintf(msr->mp, " [data \"%s\"]", + log_escape_hex(msr->mp, (unsigned char *)var->value, var->value_len)); + + /* If it is > 512 bytes, then truncate at 512 with ellipsis. + * NOTE: 512 actual data + 9 bytes of label = 521 + */ + if (strlen(logdata) > 521) { + logdata[517] = '.'; + logdata[518] = '.'; + logdata[519] = '.'; + logdata[520] = '"'; + logdata[521] = ']'; + logdata[522] = '\0'; + } + } + if ((actionset->severity >= 0)&&(actionset->severity <= 7)) { + severity = apr_psprintf(msr->mp, " [severity \"%s\"]", + msre_format_severity(actionset->severity)); + } + + /* Extract rule tags from the action list. */ + tarr = apr_table_elts(actionset->actions); + telts = (const apr_table_entry_t*)tarr->elts; + + for (k = 0; k < tarr->nelts; k++) { + msre_action *action = (msre_action *)telts[k].val; + if (strcmp(telts[k].key, "tag") == 0) { + tags = apr_psprintf(msr->mp, "%s [tag \"%s\"]", tags, + log_escape(msr->mp, action->param)); + } + } + + return apr_pstrcat(msr->mp, fn, id, rev, msg, logdata, severity, tags, NULL); +} + +char * msre_rule_generate_unparsed(apr_pool_t *pool, const msre_rule *rule, const char *targets, + const char *args, const char *actions) +{ + char *unparsed = NULL; + const char *r_targets = targets; + const char *r_args = args; + const char *r_actions = actions; + + if (r_targets == NULL) { + r_targets = rule->p1; + } + if (r_args == NULL) { + r_args = apr_pstrcat(pool, (rule->op_negated ? "!" : ""), "@", rule->op_name, " ", rule->op_param, NULL); + } + if (r_actions == NULL) { + r_actions = msre_actionset_generate_action_string(pool, rule->actionset); + } + + switch (rule->type) { + case RULE_TYPE_NORMAL: + if (r_actions == NULL) { + unparsed = apr_psprintf(pool, "SecRule \"%s\" \"%s\"", + log_escape(pool, r_targets), log_escape(pool, r_args)); + } + else { + unparsed = apr_psprintf(pool, "SecRule \"%s\" \"%s\" \"%s\"", + log_escape(pool, r_targets), log_escape(pool, r_args), + log_escape(pool, r_actions)); + } + break; + case RULE_TYPE_ACTION: + unparsed = apr_psprintf(pool, "SecAction \"%s\"", + log_escape(pool, r_actions)); + break; + case RULE_TYPE_MARKER: + unparsed = apr_psprintf(pool, "SecMarker \"%s\"", rule->actionset->id); + break; + #if defined(WITH_LUA) + case RULE_TYPE_LUA: + /* SecRuleScript */ + if (r_actions == NULL) { + unparsed = apr_psprintf(pool, "SecRuleScript \"%s\"", r_args); + } + else { + unparsed = apr_psprintf(pool, "SecRuleScript \"%s\" \"%s\"", + r_args, log_escape(pool, r_actions)); + } + break; + #endif + } + + return unparsed; +} + +/** + * Assembles a new rule using the strings that contain a list + * of targets (variables), arguments, and actions. + */ +msre_rule *msre_rule_create(msre_ruleset *ruleset, int type, + const char *fn, int line, const char *targets, + const char *args, const char *actions, char **error_msg) +{ + msre_rule *rule; + char *my_error_msg; + const char *argsp; + int rc; + + if (error_msg == NULL) return NULL; + *error_msg = NULL; + + rule = (msre_rule *)apr_pcalloc(ruleset->mp, sizeof(msre_rule)); + if (rule == NULL) return NULL; + + rule->type = type; + rule->ruleset = ruleset; + rule->targets = apr_array_make(ruleset->mp, 10, sizeof(const msre_var *)); + rule->p1 = apr_pstrdup(ruleset->mp, targets); + rule->filename = apr_pstrdup(ruleset->mp, fn); + rule->line_num = line; + + /* Parse targets */ + rc = msre_parse_targets(ruleset, targets, rule->targets, &my_error_msg); + if (rc < 0) { + *error_msg = apr_psprintf(ruleset->mp, "Error creating rule: %s", my_error_msg); + return NULL; + } + + /* Parse args */ + argsp = args; + + /* Is negation used? */ + if (*argsp == '!') { + rule->op_negated = 1; + argsp++; + while((isspace(*argsp))&&(*argsp != '\0')) argsp++; + } + + /* Is the operator explicitly selected? */ + if (*argsp != '@') { + /* Go with a regular expression. */ + rule->op_name = "rx"; + rule->op_param = argsp; + } else { + /* Explicitly selected operator. */ + char *p = (char *)(argsp + 1); + while((!isspace(*p))&&(*p != '\0')) p++; + rule->op_name = apr_pstrmemdup(ruleset->mp, argsp + 1, p - (argsp + 1)); + while(isspace(*p)) p++; /* skip over the whitespace at the end*/ + rule->op_param = p; /* IMP1 So we always have a parameter even when it's empty? */ + } + + /* Find the operator. */ + rule->op_metadata = msre_engine_op_resolve(ruleset->engine, rule->op_name); + if (rule->op_metadata == NULL) { + *error_msg = apr_psprintf(ruleset->mp, + "Error creating rule: Failed to resolve operator: %s", rule->op_name); + return NULL; + } + + /* Initialise & validate parameter */ + if (rule->op_metadata->param_init != NULL) { + if (rule->op_metadata->param_init(rule, &my_error_msg) <= 0) { + *error_msg = apr_psprintf(ruleset->mp, "Error creating rule: %s", my_error_msg); + return NULL; + } + } + + /* Parse actions */ + if (actions != NULL) { + /* Create per-rule actionset */ + rule->actionset = msre_actionset_create(ruleset->engine, actions, &my_error_msg); + if (rule->actionset == NULL) { + *error_msg = apr_psprintf(ruleset->mp, "Error parsing actions: %s", my_error_msg); + return NULL; + } + } + + /* Add the unparsed rule */ + rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, targets, args, NULL); + + return rule; +} + +#if defined(WITH_LUA) +/** + * + */ +msre_rule *msre_rule_lua_create(msre_ruleset *ruleset, + const char *fn, int line, const char *script_filename, + const char *actions, char **error_msg) +{ + msre_rule *rule; + char *my_error_msg; + + if (error_msg == NULL) return NULL; + *error_msg = NULL; + + rule = (msre_rule *)apr_pcalloc(ruleset->mp, sizeof(msre_rule)); + if (rule == NULL) return NULL; + + rule->type = RULE_TYPE_LUA; + rule->ruleset = ruleset; + rule->filename = apr_pstrdup(ruleset->mp, fn); + rule->line_num = line; + + /* Compile script. */ + *error_msg = lua_compile(&rule->script, script_filename, ruleset->mp); + if (*error_msg != NULL) { + return NULL; + } + + /* Parse actions */ + if (actions != NULL) { + /* Create per-rule actionset */ + rule->actionset = msre_actionset_create(ruleset->engine, actions, &my_error_msg); + if (rule->actionset == NULL) { + *error_msg = apr_psprintf(ruleset->mp, "Error parsing actions: %s", my_error_msg); + return NULL; + } + } + + /* Add the unparsed rule */ + rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, NULL, script_filename, NULL); + + return rule; +} +#endif + +/** + * Perform non-disruptive actions associated with the provided actionset. + */ +static void msre_perform_nondisruptive_actions(modsec_rec *msr, msre_rule *rule, + msre_actionset *actionset, apr_pool_t *mptmp) +{ + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + int i; + + tarr = apr_table_elts(actionset->actions); + telts = (const apr_table_entry_t*)tarr->elts; + for (i = 0; i < tarr->nelts; i++) { + msre_action *action = (msre_action *)telts[i].val; + if (action->metadata->type == ACTION_NON_DISRUPTIVE) { + if (action->metadata->execute != NULL) { + action->metadata->execute(msr, mptmp, rule, action); + } + } + } +} + +/** + * Perform the disruptive actions associated with the given actionset. + */ +static void msre_perform_disruptive_actions(modsec_rec *msr, msre_rule *rule, + msre_actionset *actionset, apr_pool_t *mptmp, const char *message) +{ + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + int i; + + /* Execute the disruptive actions. Do note that this does + * not mean the request will be interrupted straight away. All + * disruptive actions need to do here is update the information + * that will be used to act later. + */ + tarr = apr_table_elts(actionset->actions); + telts = (const apr_table_entry_t*)tarr->elts; + for (i = 0; i < tarr->nelts; i++) { + msre_action *action = (msre_action *)telts[i].val; + if (action->metadata->type == ACTION_DISRUPTIVE) { + if (action->metadata->execute != NULL) { + action->metadata->execute(msr, mptmp, rule, action); + } + } + } + + /* If "noauditlog" was used do not mark the transaction relevant. */ + if (actionset->auditlog != 0) { + msr->is_relevant++; + } + + /* We only do stuff when in ONLINE mode. In all other + * cases we only emit warnings. + */ + if ((msr->phase == PHASE_LOGGING) + || (msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) + || (msr->modsecurity->processing_mode == MODSEC_OFFLINE) + || (actionset->intercept_action == ACTION_NONE)) + { + int log_level; + + /* If "nolog" was used log at a higher level to prevent an "alert". */ + if (actionset->log == 0) { + log_level = 4; + + /* But, if "auditlog" is enabled, then still add the message. */ + if (actionset->auditlog != 0) { + *(const char **)apr_array_push(msr->alerts) = msc_alert_message(msr, actionset, NULL, message); + } + + } + else { + log_level = 2; + } + + msc_alert(msr, log_level, actionset, "Warning.", message); + + /* However, this will mark the txn relevant again if it is <= 3, + * which will mess up noauditlog. We need to compensate for this + * so that we do not increment twice when auditlog is enabled and + * prevent incrementing when auditlog is disabled. + */ + if (log_level <= 3) { + msr->is_relevant--; + } + + return; + } + + /* Signal to the engine we need to intercept this + * transaction, and rememer the rule that caused it. + */ + msr->was_intercepted = 1; + msr->rule_was_intercepted = 1; + msr->intercept_phase = msr->phase; + msr->intercept_actionset = actionset; + msr->intercept_message = message; +} + +/** + * Invokes the rule operator against the given value. + */ +static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, + msre_actionset *acting_actionset, apr_pool_t *mptmp) +{ + apr_time_t time_before_op = 0; + char *my_error_msg = NULL; + const char *full_varname = NULL; + int rc; + + /* determine the full var name if not already resolved + * + * NOTE: this can happen if the var does not match but it is + * being tested for non-existance as in: + * @REQUEST_HEADERS:Foo "@eq 0" + * @REQUEST_HEADERS:Foo "!@eq 1" + */ + if ((var->param != NULL) && (var->name != NULL) && (strchr(var->name,':') == NULL)) { + full_varname = apr_psprintf(mptmp, "%s%s:%s", + (var->is_counting ? "&" : ""), + var->name, var->param); + } + else if ((var->name != NULL) && var->is_counting && (*var->name != '&')) { + full_varname = apr_pstrcat(mptmp, "&", var->name, NULL); + } + else { + full_varname = var->name; + } + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Executing operator \"%s%s\" with param \"%s\" against %s.", + (rule->op_negated ? "!" : ""), rule->op_name, + log_escape(msr->mp, rule->op_param), full_varname); + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Target value: \"%s\"", log_escape_nq_ex(msr->mp, var->value, + var->value_len)); + } + + #if defined(PERFORMANCE_MEASUREMENT) + time_before_op = apr_time_now(); + #else + if (msr->txcfg->debuglog_level >= 4) { + time_before_op = apr_time_now(); + } + #endif + + rc = rule->op_metadata->execute(msr, rule, var, &my_error_msg); + + #if defined(PERFORMANCE_MEASUREMENT) + { + /* Record performance but do not log anything. */ + apr_time_t t1 = apr_time_now(); + rule->op_time += (t1 - time_before_op); + } + #else + if (msr->txcfg->debuglog_level >= 4) { + apr_time_t t1 = apr_time_now(); + msr_log(msr, 4, "Operator completed in %" APR_TIME_T_FMT " usec.", (t1 - time_before_op)); + } + #endif + + if (rc < 0) { + msr_log(msr, 4, "Operator error: %s", my_error_msg); + return -1; + } + + if (((rc == 0)&&(rule->op_negated == 0))||((rc == 1)&&(rule->op_negated == 1))) { + /* No match, do nothing. */ + return RULE_NO_MATCH; + } + else { + /* Match. */ + if (rc == 0) { + char *op_param = log_escape(msr->mp, rule->op_param); + + /* Truncate op parameter. */ + if (strlen(op_param) > 252) { + op_param = apr_psprintf(msr->mp, "%.252s ...", op_param); + } + + /* Operator did not match so we need to provide a message. */ + my_error_msg = apr_psprintf(msr->mp, "Match of \"%s %s\" against \"%s\" required.", + log_escape(msr->mp, rule->op_name), op_param, + log_escape(msr->mp, full_varname)); + } + + /* Save the rules that match */ + *(const msre_rule **)apr_array_push(msr->matched_rules) = rule; + + /* Save the last matched var data */ + msr->matched_var->name = apr_pstrdup(msr->mp, var->name); + msr->matched_var->name_len = strlen(msr->matched_var->name); + msr->matched_var->value = apr_pmemdup(msr->mp, var->value, var->value_len); + msr->matched_var->value_len = var->value_len; + + /* Keep track of the highest severity matched so far */ + if ((acting_actionset->severity > 0) && (acting_actionset->severity < msr->highest_severity)) + { + msr->highest_severity = acting_actionset->severity; + } + + + /* Perform non-disruptive actions. */ + msre_perform_nondisruptive_actions(msr, rule, rule->actionset, mptmp); + + /* Perform disruptive actions, but only if + * this rule is not part of a chain. + */ + if (rule->actionset->is_chained == 0) { + msre_perform_disruptive_actions(msr, rule, acting_actionset, mptmp, my_error_msg); + } + + return RULE_MATCH; + } +} + +/** + * Executes rule against the given transaction. + */ +static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + msre_actionset *acting_actionset = NULL; + msre_var **targets = NULL; + apr_pool_t *mptmp = msr->msc_rule_mptmp; + apr_table_t *tartab = NULL; + apr_table_t *vartab = NULL; + int i, rc = 0, match_count = 0; + int invocations = 0; + int multi_match = 0; + + /* Choose the correct metadata/disruptive action actionset. */ + acting_actionset = rule->actionset; + if (rule->chain_starter != NULL) { + acting_actionset = rule->chain_starter->actionset; + } + + /* Configure recursive matching. */ + if (apr_table_get(rule->actionset->actions, "multiMatch") != NULL) { + multi_match = 1; + } + + /* ENH: What is a good initial size? */ + tartab = apr_table_make(mptmp, 24); + if (tartab == NULL) return -1; + vartab = apr_table_make(mptmp, 24); + if (vartab == NULL) return -1; + + /* Expand variables to create a list of targets. */ + + targets = (msre_var **)rule->targets->elts; + for (i = 0; i < rule->targets->nelts; i++) { + int j, list_count; + + apr_table_clear(vartab); + + /* ENH Introduce a new variable hook that would allow the code + * behind the variable to return the size of the collection + * without having to generate the variables. + */ + + /* Expand individual variables first. */ + list_count = targets[i]->metadata->generate(msr, targets[i], rule, vartab, mptmp); + + if (targets[i]->is_counting) { + /* Count how many there are and just add the score to the target list. */ + msre_var *newvar = (msre_var *)apr_pmemdup(mptmp, targets[i], sizeof(msre_var)); + newvar->value = apr_psprintf(mptmp, "%d", list_count); + newvar->value_len = strlen(newvar->value); + apr_table_addn(tartab, newvar->name, (void *)newvar); + } else { + /* And either add them or remove from the final target list. */ + arr = apr_table_elts(vartab); + te = (apr_table_entry_t *)arr->elts; + for(j = 0; j < arr->nelts; j++) { + if (targets[i]->is_negated == 0) { + apr_table_addn(tartab, te[j].key, te[j].val); + } else { + apr_table_unset(tartab, te[j].key); + } + } + } + } + + /* Log the target variable expansion */ + if (msr->txcfg->debuglog_level >= 4) { + const char *expnames = NULL; + + arr = apr_table_elts(tartab); + if (arr->nelts > 1) { + te = (apr_table_entry_t *)arr->elts; + expnames = apr_pstrdup(mptmp, ((msre_var *)te[0].val)->name); + for(i = 1; i < arr->nelts; i++) { + expnames = apr_psprintf(mptmp, "%s|%s", expnames, ((msre_var *)te[i].val)->name); + } + if (strcmp(rule->p1, expnames) != 0) { + msr_log(msr, 4, "Expanded \"%s\" to \"%s\".", rule->p1, expnames); + } + } + } + + /* Loop through targets on the final target list, + * perform transformations as necessary, and invoke + * the operator. + */ + + arr = apr_table_elts(tartab); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + int changed; + int usecache = 0; + apr_table_t *cachetab = NULL; + apr_time_t time_before_trans = 0; + msre_var *var; + + /* Take one target. */ + var = (msre_var *)te[i].val; + + /* Is this var cacheable? */ + if (msr->txcfg->cache_trans != MODSEC_CACHE_DISABLED) { + usecache = 1; + + /* Counting vars are not cacheable due to them being created + * in a local per-rule pool. + */ + if (var->is_counting) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CACHE: Disabled - &%s is dynamic", var->name); + } + + usecache = 0; + } + /* Only cache if if the variable is available in this phase */ + else if (msr->phase < var->metadata->availability) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CACHE: Disabled - %s is not yet available in phase %d (requires phase %d or later)", var->name, msr->phase, var->metadata->availability); + } + + usecache = 0; + } + /* check the cache options */ + else if (var->value_len < msr->txcfg->cache_trans_min) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CACHE: Disabled - %s value length=%u, smaller than minlen=%" APR_SIZE_T_FMT, var->name, var->value_len, msr->txcfg->cache_trans_min); + } + + usecache = 0; + } + else if ((msr->txcfg->cache_trans_max != 0) && (var->value_len > msr->txcfg->cache_trans_max)) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CACHE: Disabled - %s value length=%u, larger than maxlen=%" APR_SIZE_T_FMT, var->name, var->value_len, msr->txcfg->cache_trans_max); + } + + usecache = 0; + } + + /* if cache is still enabled, check the VAR for cacheablity */ + if (usecache) { + if (var->metadata->is_cacheable == VAR_CACHE) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CACHE: Enabled"); + } + + #ifdef CACHE_DEBUG + msr_log(msr, 9, "CACHE: Fetching cache entry from hash=%pp: %pp=%s", msr->tcache, var, var->name); + #endif + + /* Fetch cache table for this target */ + cachetab = (apr_table_t *)apr_hash_get(msr->tcache, var->value, sizeof(var->value)); + + /* Create an empty cache table if this is the first time */ + #ifdef CACHE_DEBUG + if (cachetab) { + msr_log(msr, 9, "CACHE: Using cache table %pp", cachetab); + } + else + #else + if (cachetab == NULL) + #endif + { + /* NOTE: We use the pointer to the var value as a hash + * key as it is unique. This pointer *must* + * remain valid through the entire phase. If + * it does not, then we will not receive a cache + * hit and just wasted RAM. So, it is important + * that any such vars be marked as VAR_DONT_CACHE. + * + * ENH: Only use pointer for non-scalar vars + */ + cachetab = apr_table_make(msr->mp, 3); + apr_hash_set(msr->tcache, var->value, sizeof(var->value), cachetab); + + #ifdef CACHE_DEBUG + msr_log(msr, 9, "CACHE: Created a new cache table %pp for %pp", cachetab, var->value); + #endif + } + + } + else { + usecache = 0; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CACHE: %s transformations are not cacheable", var->name); + } + } + } + } + + #if defined(PERFORMANCE_MEASUREMENT) + time_before_trans = apr_time_now(); + #else + if (msr->txcfg->debuglog_level >= 4) { + time_before_trans = apr_time_now(); + } + #endif + + /* Transform target. */ + { + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + const char *tfnspath = NULL; + char *tfnskey = NULL; + int tfnscount = 0; + int last_cached_tfn = 0; + msre_cache_rec *crec = NULL; + msre_cache_rec *last_crec = NULL; + int k; + msre_action *action; + msre_tfn_metadata *metadata; + apr_table_t *normtab; + const char *lastvarval = NULL; + apr_size_t lastvarlen = 0; + + changed = 0; + normtab = apr_table_make(mptmp, 10); + if (normtab == NULL) return -1; + tarr = apr_table_elts(rule->actionset->actions); + telts = (const apr_table_entry_t*)tarr->elts; + + /* Build the final list of transformation functions. */ + for (k = 0; k < tarr->nelts; k++) { + action = (msre_action *)telts[k].val; + + if (strcmp(telts[k].key, "t") == 0) { + if (strcmp(action->param, "none") == 0) { + apr_table_clear(normtab); + tfnspath = NULL; + tfnskey = NULL; + tfnscount = 0; + last_crec = NULL; + last_cached_tfn = 0; + continue; + } + + if (action->param_plusminus == NEGATIVE_VALUE) { + apr_table_unset(normtab, action->param); + } + else { + tfnscount++; + + apr_table_addn(normtab, action->param, (void *)action); + + /* Check the cache, saving the 'most complete' as a + * starting point + */ + if (usecache) { + tfnspath = apr_psprintf(mptmp, "%s%s%s", (tfnspath?tfnspath:""), (tfnspath?",":""), action->param); + tfnskey = apr_psprintf(mptmp, "%x;%s", tfnscount, tfnspath); + crec = (msre_cache_rec *)apr_table_get(cachetab, tfnskey); + + #ifdef CACHE_DEBUG + msr_log(msr, 9, "CACHE: %s %s cached=%d", var->name, tfnskey, (crec ? 1 : 0)); + #endif + + if (crec != NULL) { + last_crec = crec; + last_cached_tfn = tfnscount; + } + } + } + } + } + + /* If the last cached tfn is the last in the list + * then we can stop here and just execute the action immediatly + */ + if (usecache && !multi_match && + (crec != NULL) && (crec == last_crec)) + { + crec->hits++; + + if (crec->changed) { + var->value = apr_pmemdup(mptmp, crec->val, crec->val_len); + var->value_len = crec->val_len; + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "T (%d) %s: \"%s\" [fully cached hits=%d]", crec->changed, crec->path, + log_escape_nq_ex(mptmp, var->value, var->value_len), crec->hits); + } + + #if defined(PERFORMANCE_MEASUREMENT) + { + apr_time_t t1 = apr_time_now(); + rule->trans_time += (t1 - time_before_trans); + } + #else + if (msr->txcfg->debuglog_level >= 4) { + apr_time_t t1 = apr_time_now(); + + msr_log(msr, 4, "Transformation completed in %" APR_TIME_T_FMT " usec.", + (t1 - time_before_trans)); + } + #endif + + rc = execute_operator(var, rule, msr, acting_actionset, mptmp); + + if (rc < 0) { + return -1; + } + + if (rc == RULE_MATCH) { + match_count++; + + /* Return straight away if the transaction + * was intercepted - no need to process the remaining + * targets. + */ + if (msr->rule_was_intercepted) { + return RULE_MATCH; + } + } + + continue; /* next target */ + } + + + /* Perform transformations. */ + + tarr = apr_table_elts(normtab); + + /* Execute transformations in a loop. */ + + /* Start after the last known cached transformation if we can */ + if (!multi_match && (last_crec != NULL)) { + k = last_cached_tfn; + tfnspath = last_crec->path; + last_crec->hits++; + + if ((changed = last_crec->changed) > 0) { + var->value = last_crec->val; + var->value_len = last_crec->val_len; + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "T (%d) %s: \"%s\" [partially cached hits=%d]", last_crec->changed, + tfnspath, log_escape_nq_ex(mptmp, var->value, var->value_len), last_crec->hits); + } + } + else { + tfnspath = NULL; + k = 0; + } + + /* Make a copy of the value so that we can change it in-place. */ + if (tarr->nelts) { + var->value = apr_pstrmemdup(mptmp, var->value, var->value_len); + /* var->value_len remains the same */ + } + + telts = (const apr_table_entry_t*)tarr->elts; + for (; k < tarr->nelts; k++) { + char *rval = NULL; + long int rval_length = -1; + int tfnchanged = 0; + + /* In multi-match mode we execute the operator + * once at the beginning and then once every + * time the variable is changed by the transformation + * function. + */ + if (multi_match && (k == 0 || tfnchanged)) { + invocations++; + + #if defined(PERFORMANCE_MEASUREMENT) + { + apr_time_t t1 = apr_time_now(); + rule->trans_time += (t1 - time_before_trans); + } + #else + if (msr->txcfg->debuglog_level >= 4) { + apr_time_t t1 = apr_time_now(); + + msr_log(msr, 4, "Transformation completed in %" APR_TIME_T_FMT " usec.", + (t1 - time_before_trans)); + } + #endif + + rc = execute_operator(var, rule, msr, acting_actionset, mptmp); + + if (rc < 0) { + return -1; + } + + if (rc == RULE_MATCH) { + match_count++; + + /* Return straight away if the transaction + * was intercepted - no need to process the remaining + * targets. + */ + if (msr->rule_was_intercepted) { + return RULE_MATCH; + } + } + } + + /* Perform one transformation. */ + action = (msre_action *)telts[k].val; + metadata = (msre_tfn_metadata *)action->param_data; + tfnchanged = metadata->execute(mptmp, + (unsigned char *)var->value, var->value_len, + &rval, &rval_length); + + if (tfnchanged < 0) { + return -1; + } + + if (tfnchanged) { + changed++; + } + + /* Use the new values */ + var->value = rval; + var->value_len = rval_length; + + /* Cache the transformation */ + if (usecache) { + int tfnsnum = k + 1; + + /* Generate the cache key */ + tfnspath = apr_psprintf(msr->mp, "%s%s%s", (tfnspath ? tfnspath : ""), + (tfnspath ? "," : ""), action->param); + tfnskey = apr_psprintf(msr->mp, "%x;%s", tfnsnum, tfnspath); + + if ((msr->txcfg->cache_trans_maxitems != 0) && + (msr->tcache_items >= msr->txcfg->cache_trans_maxitems)) + { + /* Warn only once if we attempt to go over the cache limit. */ + if (msr->tcache_items == msr->txcfg->cache_trans_maxitems) { + msr->tcache_items++; + msr_log(msr, 4, "CACHE: Disabled - phase=%d" + " maxitems=%" APR_SIZE_T_FMT + " limit reached.", + msr->phase, + msr->txcfg->cache_trans_maxitems); + } + } + else if (msr->txcfg->cache_trans_incremental || + (tfnsnum == tarr->nelts)) + { + /* ENH1: Add flag to vars to tell which ones can change across phases store the rest in a global cache */ + crec = (msre_cache_rec *)apr_pcalloc(msr->mp, sizeof(msre_cache_rec)); + if (crec == NULL) return -1; + + crec->hits = 0; + crec->changed = changed; + crec->num = k + 1; + crec->path = tfnspath; + + /* We want to cache a copy if it changed otherwise + * we just want to use a pointer to the last changed value. + */ + crec->val = (!lastvarval || tfnchanged) ? apr_pmemdup(msr->mp, var->value, var->value_len) : lastvarval; + crec->val_len = changed ? ((!lastvarval || tfnchanged) ? var->value_len : lastvarlen) : 0; + + /* Keep track of the last changed var value */ + if (tfnchanged) { + lastvarval = crec->val; + lastvarlen = crec->val_len; + } + + #ifdef CACHE_DEBUG + if (changed) { + msr_log(msr, 9, "CACHE: Caching %s=\"%s\" (%pp)", + tfnskey, + log_escape_nq_ex(mptmp, + crec->val, + crec->val_len), + var); + } + else { + msr_log(msr, 9, "CACHE: Caching %s= (%pp)", + tfnskey, + var); + } + #endif + + msr->tcache_items++; + + apr_table_setn(cachetab, tfnskey, (void *)crec); + } + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, metadata->name, + log_escape_nq_ex(mptmp, var->value, var->value_len)); + } + } + } + + /* Execute operator if multi-matching is not enabled, + * or if it is and we need to process the result of the + * last transformation. + */ + if (!multi_match || changed) { + invocations++; + + #if defined(PERFORMANCE_MEASUREMENT) + { + apr_time_t t1 = apr_time_now(); + rule->trans_time += (t1 - time_before_trans); + } + #else + if (msr->txcfg->debuglog_level >= 4) { + apr_time_t t1 = apr_time_now(); + + msr_log(msr, 4, "Transformation completed in %" APR_TIME_T_FMT " usec.", + (t1 - time_before_trans)); + } + #endif + + rc = execute_operator(var, rule, msr, acting_actionset, mptmp); + + if (rc < 0) { + return -1; + } + + if (rc == RULE_MATCH) { + match_count++; + + /* Return straight away if the transaction + * was intercepted - no need to process the remaining + * targets. + */ + if (msr->rule_was_intercepted) { + return RULE_MATCH; + } + } + } + } + + + return (match_count ? RULE_MATCH : RULE_NO_MATCH); +} + +#if defined(WITH_LUA) +/** + * + */ +static apr_status_t msre_rule_process_lua(msre_rule *rule, modsec_rec *msr) { + msre_actionset *acting_actionset = NULL; + char *my_error_msg = NULL; + int rc; + + /* Choose the correct metadata/disruptive action actionset. */ + acting_actionset = rule->actionset; + if (rule->chain_starter != NULL) { + acting_actionset = rule->chain_starter->actionset; + } + + rc = lua_execute(rule->script, NULL, msr, rule, &my_error_msg); + if (rc < 0) { + msr_log(msr, 1, "%s", my_error_msg); + return -1; + } + + /* A non-NULL error message means the rule matched. */ + if (my_error_msg != NULL) { + /* Perform non-disruptive actions. */ + msre_perform_nondisruptive_actions(msr, rule, rule->actionset, msr->msc_rule_mptmp); + + /* Perform disruptive actions, but only if + * this rule is not part of a chain. + */ + if (rule->actionset->is_chained == 0) { + msre_perform_disruptive_actions(msr, rule, acting_actionset, msr->msc_rule_mptmp, my_error_msg); + } + } + + return rc; +} +#endif + +/** + * + */ +apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { + /* Use a fresh memory sub-pool for processing each rule */ + if (msr->msc_rule_mptmp == NULL) { + if (apr_pool_create(&msr->msc_rule_mptmp, msr->mp) != APR_SUCCESS) { + return -1; + } + } else { + apr_pool_clear(msr->msc_rule_mptmp); + } + + #if defined(WITH_LUA) + if (rule->type == RULE_TYPE_LUA) { + return msre_rule_process_lua(rule, msr); + } + #endif + + return msre_rule_process_normal(rule, msr); +} + +/** + * Checks whether the given rule ID is in the given range. + */ +int rule_id_in_range(int ruleid, const char *range) { + char *p = NULL, *saveptr = NULL; + char *data = NULL; + + if (range == NULL) return 0; + data = strdup(range); + if (data == NULL) return 0; + + p = apr_strtok(data, ",", &saveptr); + while(p != NULL) { + char *s = strstr(p, "-"); + if (s == NULL) { + if (ruleid == atoi(p)) { + free(data); + return 1; + } + } else { + int start = atoi(p); + int end = atoi(s + 1); + if ((ruleid >= start)&&(ruleid <= end)) { + free(data); + return 1; + } + } + p = apr_strtok(NULL, ",", &saveptr); + } + + free(data); + + return 0; +} diff --git a/2.5.13/2.5.x/apache2/re.h b/2.5.13/2.5.x/apache2/re.h new file mode 100644 index 00000000..24eb47e3 --- /dev/null +++ b/2.5.13/2.5.x/apache2/re.h @@ -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 diff --git a/2.5.13/2.5.x/apache2/re_actions.c b/2.5.13/2.5.x/apache2/re_actions.c new file mode 100644 index 00000000..ea50e160 --- /dev/null +++ b/2.5.13/2.5.x/apache2/re_actions.c @@ -0,0 +1,2421 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2008 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 "re.h" +#include + +/** + * Register action with the engine. + */ +static void msre_engine_action_register(msre_engine *engine, const char *name, + unsigned int type, unsigned int argc_min, unsigned int argc_max, + unsigned int allow_param_plusminus, unsigned int cardinality, + unsigned int cardinality_group, fn_action_validate_t validate, + fn_action_init_t init, fn_action_execute_t execute) +{ + msre_action_metadata *metadata = (msre_action_metadata *)apr_pcalloc(engine->mp, + sizeof(msre_action_metadata)); + if (metadata == NULL) return; + + metadata->name = name; + metadata->type = type; + metadata->argc_min = argc_min; + metadata->argc_max = argc_max; + metadata->allow_param_plusminus = allow_param_plusminus; + metadata->cardinality = cardinality; + metadata->cardinality_group = cardinality_group; + metadata->validate = validate; + metadata->init = init; + metadata->execute = execute; + + apr_table_setn(engine->actions, name, (void *)metadata); +} + +/** + * Generates a single variable (from the supplied metadata). + */ +msre_var *generate_single_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, + msre_rule *rule, apr_pool_t *mptmp) +{ + apr_table_t *vartab = NULL; + const apr_table_entry_t *te = NULL; + const apr_array_header_t *arr = NULL; + msre_var *rvar = NULL; + int i; + + /* Sanity check. */ + if ((var == NULL)||(var->metadata == NULL)||(var->metadata->generate == NULL)) return NULL; + + vartab = apr_table_make(mptmp, 16); + var->metadata->generate(msr, var, rule, vartab, mptmp); + + arr = apr_table_elts(vartab); + if (arr->nelts == 0) return NULL; + te = (apr_table_entry_t *)arr->elts; + + rvar = (msre_var *)te[0].val; + + /* Return straight away if there were no + * transformation functions supplied. + */ + if ((tfn_arr == NULL)||(tfn_arr->nelts == 0)) { + return rvar; + } + + /* Copy the value so that we can transform it in place. */ + rvar->value = apr_pstrndup(mptmp, rvar->value, rvar->value_len); + + /* Transform rvar in a loop. */ + for (i = 0; i < tfn_arr->nelts; i++) { + msre_tfn_metadata *tfn = ((msre_tfn_metadata **)tfn_arr->elts)[i]; + char *rval; + int rc; + long int rval_len; + + rc = tfn->execute(mptmp, (unsigned char *)rvar->value, + rvar->value_len, &rval, &rval_len); + + rvar->value = rval; + rvar->value_len = rval_len; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, + log_escape_nq_ex(mptmp, rvar->value, rvar->value_len)); + } + } + + return rvar; +} + +/** + * + */ +apr_table_t *generate_multi_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, + msre_rule *rule, apr_pool_t *mptmp) +{ + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + apr_table_t *vartab = NULL, *tvartab = NULL; + msre_var *rvar = NULL; + int i, j; + + /* Sanity check. */ + if ((var == NULL)||(var->metadata == NULL)||(var->metadata->generate == NULL)) return NULL; + + /* Generate variables. */ + vartab = apr_table_make(mptmp, 16); + var->metadata->generate(msr, var, rule, vartab, mptmp); + + /* Return straight away if there were no + * transformation functions supplied. + */ + if ((tfn_arr == NULL)||(tfn_arr->nelts == 0)) { + return vartab; + } + + tvartab = apr_table_make(mptmp, 16); + + tarr = apr_table_elts(vartab); + telts = (const apr_table_entry_t*)tarr->elts; + for (j = 0; j < tarr->nelts; j++) { + rvar = (msre_var *)telts[j].val; + + /* Copy the value so that we can transform it in place. */ + rvar->value = apr_pstrndup(mptmp, rvar->value, rvar->value_len); + + /* Transform rvar in a loop. */ + for (i = 0; i < tfn_arr->nelts; i++) { + msre_tfn_metadata *tfn = ((msre_tfn_metadata **)tfn_arr->elts)[i]; + char *rval; + int rc; + long int rval_len; + + rc = tfn->execute(mptmp, (unsigned char *)rvar->value, + rvar->value_len, &rval, &rval_len); + + rvar->value = rval; + rvar->value_len = rval_len; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, + log_escape_nq_ex(mptmp, rvar->value, rvar->value_len)); + } + } + + apr_table_addn(tvartab, rvar->name, (void *)rvar); + } + + return tvartab; +} + +/** + * Expands macros ("%{NAME}" entities) if present + * in the given variable. + */ +int expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t *mptmp) { + char *data = NULL; + apr_array_header_t *arr = NULL; + char *p = NULL, *q = NULL, *t = NULL; + char *text_start = NULL, *next_text_start = NULL; + msc_string *part = NULL; + int i, offset = 0; + + if (var->value == NULL) return 0; + + /* IMP1 Duplicate the string and create the array on + * demand, thus not having to do it if there are + * no macros in the input data. + */ + + data = apr_pstrdup(mptmp, var->value); /* IMP1 Are we modifying data anywhere? */ + arr = apr_array_make(mptmp, 16, sizeof(msc_string *)); + if ((data == NULL)||(arr == NULL)) return -1; + + text_start = next_text_start = data; + do { + text_start = next_text_start; + p = strstr(text_start, "%"); + if (p != NULL) { + char *var_name = NULL; + char *var_value = NULL; + + if ((*(p + 1) == '{')&&(*(p + 2) != '\0')) { + char *var_start = p + 2; + + t = var_start; + while((*t != '\0')&&(*t != '}')) t++; + if (*t == '}') { + /* Named variable. */ + + var_name = apr_pstrmemdup(mptmp, var_start, t - var_start); + q = strstr(var_name, "."); + if (q != NULL) { + var_value = q + 1; + *q = '\0'; + } + + next_text_start = t + 1; /* *t was '}' */ + } else { + /* Warn about a possiblly forgotten '}' */ + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Warning: Possibly unterminated macro: \"%s\"", + log_escape_ex(mptmp, var_start - 2, t - var_start + 2)); + } + + next_text_start = t; /* *t was '\0' */ + } + } + + if (var_name != NULL) { + char *my_error_msg = NULL; + msre_var *var_generated = NULL; + msre_var *var_resolved = NULL; + + /* Add the text part before the macro to the array. */ + part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string)); + if (part == NULL) return -1; + part->value_len = p - text_start; + part->value = apr_pstrmemdup(mptmp, text_start, part->value_len); + *(msc_string **)apr_array_push(arr) = part; + + /* Resolve the macro and add that to the array. */ + var_resolved = msre_create_var_ex(mptmp, msr->modsecurity->msre, var_name, var_value, + msr, &my_error_msg); + if (var_resolved != NULL) { + var_generated = generate_single_var(msr, var_resolved, NULL, rule, mptmp); + if (var_generated != NULL) { + part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string)); + if (part == NULL) return -1; + part->value_len = var_generated->value_len; + part->value = (char *)var_generated->value; + *(msc_string **)apr_array_push(arr) = part; + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Resolved macro %%{%s%s%s} to: %s", + var_name, + (var_value ? "." : ""), + (var_value ? var_value : ""), + log_escape_nq_ex(mptmp, part->value, part->value_len)); + } + } + } else { + msr_log(msr, 4, "Failed to resolve macro %%{%s%s%s}: %s", + var_name, + (var_value ? "." : ""), + (var_value ? var_value : ""), + my_error_msg); + } + } else { + /* We could not identify a valid macro so add it as text. */ + part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string)); + if (part == NULL) return -1; + part->value_len = p - text_start + 1; /* len(text)+len("%") */ + part->value = apr_pstrmemdup(mptmp, text_start, part->value_len); + *(msc_string **)apr_array_push(arr) = part; + + next_text_start = p + 1; + } + } else { + /* Text part. */ + part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string)); + part->value = apr_pstrdup(mptmp, text_start); + part->value_len = strlen(part->value); + *(msc_string **)apr_array_push(arr) = part; + } + } while (p != NULL); + + /* If there's more than one member of the array that + * means there was at least one macro present. Combine + * text parts into a single string now. + */ + if (arr->nelts > 1) { + /* Figure out the required size for the string. */ + var->value_len = 0; + for(i = 0; i < arr->nelts; i++) { + part = ((msc_string **)arr->elts)[i]; + var->value_len += part->value_len; + } + + /* Allocate the string. */ + var->value = apr_palloc(msr->mp, var->value_len + 1); + if (var->value == NULL) return -1; + + /* Combine the parts. */ + offset = 0; + for(i = 0; i < arr->nelts; i++) { + part = ((msc_string **)arr->elts)[i]; + memcpy((char *)(var->value + offset), part->value, part->value_len); + offset += part->value_len; + } + var->value[offset] = '\0'; + } + + return 1; +} + +/** + * Record the original collection values to use to calculate deltas. + * This can be called multiple times and will not overwrite the first + * value that is set. + */ +apr_status_t collection_original_setvar(modsec_rec *msr, const char *col_name, const msc_string *orig_var) { + apr_table_t *table = NULL; + msc_string *var = NULL; + const char *var_name = NULL; + + if (orig_var == NULL) { + msr_log(msr, 1, "Internal Error: Attempt to record NULL original variable."); + return -1; + } + + var_name = orig_var->name; + table = (apr_table_t *)apr_table_get(msr->collections_original, col_name); + + /* Does the collection exist already? */ + if (table == NULL) { + table = apr_table_make(msr->mp, 24); + if (table == NULL) { + msr_log(msr, 1, "Failed to allocate space for original collection."); + return -1; + } + apr_table_setn(msr->collections_original, apr_pstrdup(msr->mp, col_name), (void *)table); + } + else { + /* Does the variable exist already? */ + var = (msc_string *)apr_table_get(table, var_name); + if (var != NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Original collection variable: %s.%s = \"%s\"", col_name, var_name, log_escape_ex(msr->mp, orig_var->value, orig_var->value_len)); + } + return 1; + } + } + + var = (msc_string *)apr_palloc(msr->mp, sizeof(msc_string)); + if (var == NULL) { + msr_log(msr, 1, "Failed to allocate space for original collection variable."); + return -1; + } + + /* Copy the original var and add to collection. */ + var->name = orig_var->name ? apr_pstrmemdup(msr->mp, orig_var->name, orig_var->name_len) : NULL; + var->name_len = orig_var->name_len; + var->value = orig_var->value ? apr_pstrmemdup(msr->mp, orig_var->value, orig_var->value_len) : NULL; + var->value_len = orig_var->value_len; + apr_table_setn(table, apr_pstrmemdup(msr->mp, var->name, var->name_len), (void *)var); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Recorded original collection variable: %s.%s = \"%s\"", col_name, var_name, log_escape_ex(msr->mp, var->value, var->value_len)); + } + + return 0; +} + +/* id */ + +static apr_status_t msre_action_id_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->id = action->param; + return 1; +} + +/* rev */ + +static apr_status_t msre_action_rev_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->rev = action->param; + return 1; +} + +/* msg */ + +static apr_status_t msre_action_msg_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->msg = action->param; + return 1; +} + +/* logdata */ + +static apr_status_t msre_action_logdata_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->logdata = action->param; + return 1; +} + +/* severity */ + +static apr_status_t msre_action_severity_init(msre_engine *engine, + msre_actionset *actionset, msre_action *action) +{ + actionset->severity = atoi(action->param); + return 1; +} + +/* chain */ + +static apr_status_t msre_action_chain_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->is_chained = 1; + return 1; +} + +/* log */ +static apr_status_t msre_action_log_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->log = 1; + return 1; +} + +/* nolog */ +static apr_status_t msre_action_nolog_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->log = 0; + actionset->auditlog = 0; + return 1; +} + +/* auditlog */ +static apr_status_t msre_action_auditlog_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->auditlog = 1; + return 1; +} + +/* noauditlog */ +static apr_status_t msre_action_noauditlog_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->auditlog = 0; + return 1; +} + +/* block */ +static apr_status_t msre_action_block_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + /* Right now we just set a flag and inherit the real disruptive action */ + actionset->block = 1; + return 1; +} + +/* deny */ +static apr_status_t msre_action_deny_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->intercept_action = ACTION_DENY; + actionset->intercept_action_rec = action; + return 1; +} + +/* status */ +static char *msre_action_status_validate(msre_engine *engine, msre_action *action) { + /* ENH action->param must be a valid HTTP status code. */ + return NULL; +} + +static apr_status_t msre_action_status_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->intercept_status = atoi(action->param); + return 1; +} + +/* drop */ +static apr_status_t msre_action_drop_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->intercept_action = ACTION_DROP; + actionset->intercept_action_rec = action; + return 1; +} + +/* pause */ +static char *msre_action_pause_validate(msre_engine *engine, msre_action *action) { + /* ENH Validate a positive number. */ + return NULL; +} + +static apr_status_t msre_action_pause_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->intercept_pause = atoi(action->param); + return 1; +} + +/* redirect */ + +static char *msre_action_redirect_validate(msre_engine *engine, msre_action *action) { + /* ENH Add validation. */ + return NULL; +} + +static apr_status_t msre_action_redirect_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->intercept_action = ACTION_REDIRECT; + actionset->intercept_uri = action->param; + actionset->intercept_action_rec = action; + return 1; +} + +static apr_status_t msre_action_redirect_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + msc_string *var = NULL; + + var = apr_pcalloc(mptmp, sizeof(msc_string)); + if (var == NULL) return -1; + var->value = (char *)action->param; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + + rule->actionset->intercept_uri = apr_pstrmemdup(msr->mp, var->value, var->value_len); + + return 1; +} + +/* proxy */ + +static char *msre_action_proxy_validate(msre_engine *engine, msre_action *action) { + /* ENH Add validation. */ + return NULL; +} + +static apr_status_t msre_action_proxy_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->intercept_action = ACTION_PROXY; + actionset->intercept_uri = action->param; + actionset->intercept_action_rec = action; + return 1; +} + +static apr_status_t msre_action_proxy_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + msc_string *var = NULL; + + var = apr_pcalloc(mptmp, sizeof(msc_string)); + if (var == NULL) return -1; + var->value = (char *)action->param; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + + rule->actionset->intercept_uri = apr_pstrmemdup(msr->mp, var->value, var->value_len); + + return 1; +} + +/* pass */ + +static apr_status_t msre_action_pass_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->intercept_action = ACTION_NONE; + actionset->intercept_action_rec = action; + return 1; +} + +/* skip */ + +static char *msre_action_skip_validate(msre_engine *engine, msre_action *action) { + /* ENH Add validation. */ + return NULL; +} + +static apr_status_t msre_action_skip_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->skip_count = atoi(action->param); + if (actionset->skip_count <= 0) actionset->skip_count = 1; + return 1; +} + +/* skipAfter */ + +static char *msre_action_skipAfter_validate(msre_engine *engine, msre_action *action) { + /* ENH Add validation. */ + return NULL; +} + +static apr_status_t msre_action_skipAfter_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->skip_after = action->param; + return 1; +} + +/* allow */ + +static apr_status_t msre_action_allow_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->intercept_action = ACTION_ALLOW; + actionset->intercept_action_rec = action; + + if (action->param != NULL) { + if (strcasecmp(action->param, "phase") == 0) { + actionset->intercept_action = ACTION_ALLOW_PHASE; + } else + if (strcasecmp(action->param, "request") == 0) { + actionset->intercept_action = ACTION_ALLOW_REQUEST; + } + } + + return 1; +} + +static char *msre_action_allow_validate(msre_engine *engine, msre_action *action) { + if (action->param != NULL) { + if (strcasecmp(action->param, "phase") == 0) { + return NULL; + } else + if (strcasecmp(action->param, "request") == 0) { + return NULL; + } else { + return apr_psprintf(engine->mp, "Invalid parameter for allow: %s", action->param); + } + } + + return NULL; +} + +/* phase */ + +static char *msre_action_phase_validate(msre_engine *engine, msre_action *action) { + /* ENH Add validation. */ + return NULL; +} + +static apr_status_t msre_action_phase_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->phase = atoi(action->param); + return 1; +} + +/* t */ + +static char *msre_action_t_validate(msre_engine *engine, msre_action *action) { + msre_tfn_metadata *metadata = NULL; + metadata = msre_engine_tfn_resolve(engine, action->param); + if (metadata == NULL) return apr_psprintf(engine->mp, "Invalid transformation function: %s", + action->param); + action->param_data = metadata; + return NULL; +} + +static apr_status_t msre_action_t_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + msre_tfn_metadata *metadata = (msre_tfn_metadata *)action->param_data; + action->param_data = metadata; + return 1; +} + +/* ctl */ +static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) { + char *name = NULL; + char *value = NULL; + + /* Parse first. */ + if (parse_name_eq_value(engine->mp, action->param, &name, &value) < 0) { + return FATAL_ERROR; + } + if (value == NULL) { + return apr_psprintf(engine->mp, "Missing ctl value for name: %s", name); + } + + /* Validate value. */ + if (strcasecmp(name, "ruleEngine") == 0) { + if (strcasecmp(value, "on") == 0) return NULL; + if (strcasecmp(value, "off") == 0) return NULL; + if (strcasecmp(value, "detectiononly") == 0) return NULL; + return apr_psprintf(engine->mp, "Invalid setting for ctl name ruleEngine: %s", value); + } else + if (strcasecmp(name, "ruleRemoveById") == 0) { + /* ENH nothing yet */ + return NULL; + } else + if (strcasecmp(name, "requestBodyAccess") == 0) { + if (parse_boolean(value) == -1) { + return apr_psprintf(engine->mp, "Invalid setting for ctl name " + " requestBodyAccess: %s", value); + } + return NULL; + } else + if (strcasecmp(name, "requestBodyProcessor") == 0) { + /* ENH We will accept anything for now but it'd be nice + * to add a check here that the processor name is a valid one. + */ + return NULL; + } else + if (strcasecmp(name, "forceRequestBodyVariable") == 0) { + if (strcasecmp(value, "on") == 0) return NULL; + if (strcasecmp(value, "off") == 0) return NULL; + return apr_psprintf(engine->mp, "Invalid setting for ctl name " + " forceRequestBodyVariable: %s", value); + } else + if (strcasecmp(name, "responseBodyAccess") == 0) { + if (parse_boolean(value) == -1) { + return apr_psprintf(engine->mp, "Invalid setting for ctl name " + " responseBodyAccess: %s", value); + } + return NULL; + } else + if (strcasecmp(name, "auditEngine") == 0) { + if (strcasecmp(value, "on") == 0) return NULL; + if (strcasecmp(value, "off") == 0) return NULL; + if (strcasecmp(value, "relevantonly") == 0) return NULL; + return apr_psprintf(engine->mp, "Invalid setting for ctl name " + " auditEngine: %s", value); + } else + if (strcasecmp(name, "auditLogParts") == 0) { + if ((value[0] == '+')||(value[0] == '-')) { + if (is_valid_parts_specification(value + 1) != 1) { + return apr_psprintf(engine->mp, "Invalid setting for ctl name " + "auditLogParts: %s", value); + } + } + else + if (is_valid_parts_specification(value) != 1) { + return apr_psprintf(engine->mp, "Invalid setting for ctl name " + "auditLogParts: %s", value); + } + return NULL; + } else + if (strcasecmp(name, "debugLogLevel") == 0) { + if ((atoi(value) >= 0)&&(atoi(value) <= 9)) return NULL; + return apr_psprintf(engine->mp, "Invalid setting for ctl name " + "debugLogLevel: %s", value); + } else + if (strcasecmp(name, "requestBodyLimit") == 0) { + long int limit = strtol(value, NULL, 10); + + if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { + return apr_psprintf(engine->mp, "Invalid setting for ctl name " + "requestBodyLimit: %s", value); + } + + if (limit > REQUEST_BODY_HARD_LIMIT) { + return apr_psprintf(engine->mp, "Request size limit cannot exceed " + "the hard limit: %ld", RESPONSE_BODY_HARD_LIMIT); + } + + return NULL; + } else + if (strcasecmp(name, "responseBodyLimit") == 0) { + long int limit = strtol(value, NULL, 10); + + if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { + return apr_psprintf(engine->mp, "Invalid setting for ctl name " + "responseBodyLimit: %s", value); + } + + if (limit > RESPONSE_BODY_HARD_LIMIT) { + return apr_psprintf(engine->mp, "Response size limit cannot exceed " + "the hard limit: %ld", RESPONSE_BODY_HARD_LIMIT); + } + + return NULL; + } + else { + return apr_psprintf(engine->mp, "Invalid ctl name setting: %s", name); + } +} + +static apr_status_t msre_action_ctl_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + /* Do nothing. */ + return 1; +} + +static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + char *name = NULL; + char *value = NULL; + + /* Parse first. */ + if (parse_name_eq_value(msr->mp, action->param, &name, &value) < 0) return -1; + if (value == NULL) return -1; + + /* Validate value. */ + if (strcasecmp(name, "ruleEngine") == 0) { + if (strcasecmp(value, "on") == 0) { + msr->txcfg->is_enabled = MODSEC_ENABLED; + msr->usercfg->is_enabled = MODSEC_ENABLED; + } + else + if (strcasecmp(value, "off") == 0) { + msr->txcfg->is_enabled = MODSEC_DISABLED; + msr->usercfg->is_enabled = MODSEC_DISABLED; + } + else + if (strcasecmp(value, "detectiononly") == 0) { + msr->txcfg->is_enabled = MODSEC_DETECTION_ONLY; + msr->usercfg->is_enabled = MODSEC_DETECTION_ONLY; + } + + return 1; + } else + if (strcasecmp(name, "ruleRemoveById") == 0) { + *(const char **)apr_array_push(msr->removed_rules) = (const char *)apr_pstrdup(msr->mp, value); + return 1; + } else + if (strcasecmp(name, "requestBodyAccess") == 0) { + int pv = parse_boolean(value); + + if (pv == -1) return -1; + msr->txcfg->reqbody_access = pv; + msr->usercfg->reqbody_access = pv; + msr_log(msr, 4, "Ctl: Set requestBodyAccess to %d.", pv); + + return 1; + } else + if (strcasecmp(name, "forceRequestBodyVariable") == 0) { + if (strcasecmp(value, "on") == 0) { + msr->txcfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_ON; + msr->usercfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_ON; + } + else + if (strcasecmp(value, "off") == 0) { + msr->txcfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_OFF; + msr->usercfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_OFF; + } + + msr_log(msr, 4, "Ctl: Set requestBodyAccess to %d.", msr->txcfg->reqbody_buffering); + + return 1; + } else + if (strcasecmp(name, "requestBodyProcessor") == 0) { + msr->msc_reqbody_processor = value; + msr_log(msr, 4, "Ctl: Set requestBodyProcessor to %s.", value); + + return 1; + } else + if (strcasecmp(name, "responseBodyAccess") == 0) { + int pv = parse_boolean(value); + + if (pv == -1) return -1; + msr->txcfg->resbody_access = pv; + msr->usercfg->resbody_access = pv; + msr_log(msr, 4, "Ctl: Set responseBodyAccess to %d.", pv); + + return 1; + } else + if (strcasecmp(name, "auditEngine") == 0) { + if (strcasecmp(value, "on") == 0) { + msr->txcfg->auditlog_flag = AUDITLOG_ON; + msr->usercfg->auditlog_flag = AUDITLOG_ON; + } + else + if (strcasecmp(value, "off") == 0) { + msr->txcfg->auditlog_flag = AUDITLOG_OFF; + msr->usercfg->auditlog_flag = AUDITLOG_OFF; + } + else + if (strcasecmp(value, "relevantonly") == 0) { + msr->txcfg->auditlog_flag = AUDITLOG_RELEVANT; + msr->usercfg->auditlog_flag = AUDITLOG_RELEVANT; + } + + msr_log(msr, 4, "Ctl: Set auditEngine to %d.", msr->txcfg->auditlog_flag); + + return 1; + } else + if (strcasecmp(name, "auditLogParts") == 0) { + char *new_value = value; + + if (value[0] == '+') { + /* Add the listed parts. */ + new_value = apr_pstrcat(msr->mp, msr->txcfg->auditlog_parts, value + 1, NULL); + } + else + if (value[0] == '-') { /* Remove the listed parts. */ + char c, *t = value + 1; + + /* Start with the current value. */ + new_value = apr_pstrdup(msr->mp, msr->txcfg->auditlog_parts); + + while((c = *t++) != '\0') { + char *s = new_value; + char *d = new_value; + + while(*s != '\0') { + if (*s != c) { + *(d++) = *(s++); + } else { + s++; + } + } + *d = '\0'; + } + } + + /* Set the new value. */ + msr->txcfg->auditlog_parts = new_value; + msr->usercfg->auditlog_parts = new_value; + msr_log(msr, 4, "Ctl: Set auditLogParts to %s.", msr->txcfg->auditlog_parts); + + return 1; + } else + if (strcasecmp(name, "debugLogLevel") == 0) { + msr->txcfg->debuglog_level = atoi(value); + msr->usercfg->debuglog_level = atoi(value); + msr_log(msr, 4, "Ctl: Set debugLogLevel to %d.", msr->txcfg->debuglog_level); + + return 1; + } else + if (strcasecmp(name, "requestBodyLimit") == 0) { + long int limit = strtol(value, NULL, 10); + + /* ENH Accept only in correct phase warn otherwise. */ + msr->txcfg->reqbody_limit = limit; + msr->usercfg->reqbody_limit = limit; + + return 1; + } else + if (strcasecmp(name, "responseBodyLimit") == 0) { + long int limit = strtol(value, NULL, 10); + + /* ENH Accept only in correct phase warn otherwise. */ + msr->txcfg->of_limit = limit; + msr->usercfg->of_limit = limit; + + return 1; + } + else { + /* Should never happen, but log if it does. */ + msr_log(msr, 1, "Internal Error: Unknown ctl action \"%s\".", name); + return -1; + } +} + +/* xmlns */ +static char *msre_action_xmlns_validate(msre_engine *engine, msre_action *action) { + char *name = NULL; + char *value = NULL; + + /* Parse first. */ + if (parse_name_eq_value(engine->mp, action->param, &name, &value) < 0) { + return FATAL_ERROR; + } + if (value == NULL) { + return apr_psprintf(engine->mp, "Missing xmlns href for prefix: %s", name); + } + + /* Don't do anything else right now, we are just storing + * the value for the variable, which is the real consumer + * for the namespace information. + */ + + return NULL; +} + +/* sanitiseArg */ +static apr_status_t msre_action_sanitiseArg_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + const char *sargname = NULL; + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + int i; + + sargname = action->param; + + tarr = apr_table_elts(msr->arguments); + telts = (const apr_table_entry_t*)tarr->elts; + for (i = 0; i < tarr->nelts; i++) { + msc_arg *arg = (msc_arg *)telts[i].val; + + if (strcasecmp(sargname, arg->name) == 0) { + apr_table_addn(msr->arguments_to_sanitise, arg->name, (void *)arg); + } + } + + return 1; +} + +#define SANITISE_ARG 1 +#define SANITISE_REQUEST_HEADER 2 +#define SANITISE_RESPONSE_HEADER 3 + +/* sanitiseMatched */ +static apr_status_t msre_action_sanitiseMatched_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + const char *sargname = NULL; + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + int i, type = 0; + msc_string *mvar = msr->matched_var; + + if (mvar->name_len == 0) return 0; + + /* IMP1 We need to extract the variable name properly here, + * taking into account it may have been escaped. + */ + if ((mvar->name_len > 5) && (strncmp(mvar->name, "ARGS:", 5) == 0)) { + sargname = apr_pstrdup(msr->mp, mvar->name + 5); + type = SANITISE_ARG; + } else + if ((mvar->name_len > 11) && (strncmp(mvar->name, "ARGS_NAMES:", 11) == 0)) { + sargname = apr_pstrdup(msr->mp, mvar->name + 11); + type = SANITISE_ARG; + } else + if ((mvar->name_len > 16) && (strncmp(mvar->name, "REQUEST_HEADERS:", 16) == 0)) { + sargname = apr_pstrdup(msr->mp, mvar->name + 16); + type = SANITISE_REQUEST_HEADER; + } else + if ((mvar->name_len > 22) && (strncmp(mvar->name, "REQUEST_HEADERS_NAMES:", 22) == 0)) { + sargname = apr_pstrdup(msr->mp, mvar->name + 22); + type = SANITISE_REQUEST_HEADER; + } else + if ((mvar->name_len > 17) && (strncmp(mvar->name, "RESPONSE_HEADERS:", 17) == 0)) { + sargname = apr_pstrdup(msr->mp, mvar->name + 17); + type = SANITISE_RESPONSE_HEADER; + } else + if ((mvar->name_len > 23) && (strncmp(mvar->name, "RESPONSE_HEADERS_NAMES:", 23) == 0)) { + sargname = apr_pstrdup(msr->mp, mvar->name + 23); + type = SANITISE_RESPONSE_HEADER; + } + else { + msr_log(msr, 3, "sanitiseMatched: Don't know how to handle variable: %s", + mvar->name); + return 0; + } + + switch(type) { + case SANITISE_ARG : + tarr = apr_table_elts(msr->arguments); + telts = (const apr_table_entry_t*)tarr->elts; + for (i = 0; i < tarr->nelts; i++) { + msc_arg *arg = (msc_arg *)telts[i].val; + if (strcasecmp(sargname, arg->name) == 0) { + apr_table_addn(msr->arguments_to_sanitise, arg->name, (void *)arg); + } + } + break; + + case SANITISE_REQUEST_HEADER : + apr_table_set(msr->request_headers_to_sanitise, sargname, "1"); + break; + + case SANITISE_RESPONSE_HEADER : + apr_table_set(msr->response_headers_to_sanitise, sargname, "1"); + break; + + default : + /* do nothing */ + break; + } + + return 1; +} + +/* sanitiseRequestHeader */ +static apr_status_t msre_action_sanitiseRequestHeader_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + apr_table_set(msr->request_headers_to_sanitise, action->param, "1"); + return 1; +} + +/* sanitiseResponseHeader */ +static apr_status_t msre_action_sanitiseResponseHeader_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + apr_table_set(msr->response_headers_to_sanitise, action->param, "1"); + return 1; +} + +/* setenv */ +static apr_status_t msre_action_setenv_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + char *data = apr_pstrdup(mptmp, action->param); + char *env_name = NULL, *env_value = NULL; + char *s = NULL; + msc_string *env = NULL; + + /* Extract the name and the value. */ + /* IMP1 We have a function for this now, parse_name_eq_value? */ + s = strstr(data, "="); + if (s == NULL) { + env_name = data; + env_value = "1"; + } else { + env_name = data; + env_value = s + 1; + *s = '\0'; + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Setting env variable: %s=%s", env_name, env_value); + } + + /* Expand and escape any macros in the name */ + env = apr_palloc(msr->mp, sizeof(msc_string)); + if (env == NULL) { + msr_log(msr, 1, "Failed to allocate space to expand name macros"); + return -1; + } + env->value = env_name; + env->value_len = strlen(env->value); + expand_macros(msr, env, rule, mptmp); + env_name = log_escape_nq_ex(msr->mp, env->value, env->value_len); + + /* Execute the requested action. */ + if (env_name[0] == '!') { + /* Delete */ + apr_table_unset(msr->r->subprocess_env, env_name + 1); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Unset env variable \"%s\".", env_name); + } + } else { + /* Set */ + char * val_value = NULL; + msc_string *val = apr_palloc(msr->mp, sizeof(msc_string)); + if (val == NULL) { + msr_log(msr, 1, "Failed to allocate space to expand value macros"); + return -1; + } + + /* Expand values in value */ + val->value = env_value; + val->value_len = strlen(val->value); + expand_macros(msr, val, rule, mptmp); + + /* To be safe, we escape NULs as it goes in subprocess_env. */ + val_value = log_escape_nul(msr->mp, (const unsigned char *)val->value, val->value_len); + + apr_table_set(msr->r->subprocess_env, env_name, val_value); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Set env variable \"%s\" to: %s", + env_name, + log_escape_nq(mptmp, val_value)); + } + } + + return 1; +} + +/* setvar */ +apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, char *var_name, char *var_value) +{ + char *col_name = NULL; + char *s = NULL; + apr_table_t *target_col = NULL; + int is_negated = 0; + msc_string *var = NULL; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Setting variable: %s=%s", var_name, var_value); + } + + /* Expand and escape any macros in the name */ + var = apr_palloc(msr->mp, sizeof(msc_string)); + if (var == NULL) { + msr_log(msr, 1, "Failed to allocate space to expand name macros"); + return -1; + } + var->value = var_name; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + var_name = log_escape_nq_ex(msr->mp, var->value, var->value_len); + + /* Handle the exclamation mark. */ + if (var_name[0] == '!') { + var_name = var_name + 1; + is_negated = 1; + } + + /* ENH Not possible to use ! and = at the same time. */ + /* ENH Not possible to change variable "KEY". */ + + /* Figure out the collection name. */ + target_col = msr->tx_vars; + s = strstr(var_name, "."); + if (s == NULL) { + msr_log(msr, 3, "Asked to set variable \"%s\", but no collection name specified. ", + log_escape(msr->mp, var_name)); + return 0; + } + col_name = var_name; + var_name = s + 1; + *s = '\0'; + + /* Locate the collection. */ + if (strcasecmp(col_name, "tx") == 0) { /* Special case for TX variables. */ + target_col = msr->tx_vars; + } else { + target_col = (apr_table_t *)apr_table_get(msr->collections, col_name); + if (target_col == NULL) { + msr_log(msr, 3, "Could not set variable \"%s.%s\" as the collection does not exist.", + log_escape(msr->mp, col_name), log_escape(msr->mp, var_name)); + return 0; + } + } + + if (is_negated) { + /* Unset variable. */ + + /* ENH Refuse to remove certain variables, e.g. TIMEOUT, internal variables, etc... */ + + apr_table_unset(target_col, var_name); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Unset variable \"%s.%s\".", col_name, var_name); + } + } + else { + /* Set or change variable. */ + + if ((var_value[0] == '+')||(var_value[0] == '-')) { + /* Relative change. */ + msc_string *rec = NULL; + msc_string *val = apr_palloc(msr->mp, sizeof(msc_string)); + int value = 0; + + if (val == NULL) { + msr_log(msr, 1, "Failed to allocate space to expand value macros"); + return -1; + } + + /* Retrieve variable or generate (if it does not exist). */ + rec = (msc_string *)apr_table_get(target_col, var_name); + if (rec == NULL) { + rec = var; /* use the already allocated space for var */ + rec->name = apr_pstrdup(msr->mp, var_name); + rec->name_len = strlen(rec->name); + value = 0; + rec->value = apr_psprintf(msr->mp, "%d", value); + rec->value_len = strlen(rec->value); + } + else { + value = atoi(rec->value); + } + + /* Record the original value before we change it */ + collection_original_setvar(msr, col_name, rec); + + /* Expand values in value */ + val->value = var_value; + val->value_len = strlen(val->value); + expand_macros(msr, val, rule, mptmp); + var_value = val->value; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Relative change: %s=%d%s", var_name, value, var_value); + } + + /* Change value. */ + value += atoi(var_value); + if (value < 0) value = 0; /* Counters never go below zero. */ + + /* Put the variable back. */ + rec->value = apr_psprintf(msr->mp, "%d", value); + rec->value_len = strlen(rec->value); + apr_table_setn(target_col, rec->name, (void *)rec); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Set variable \"%s.%s\" to \"%s\".", + col_name, rec->name, + log_escape_ex(mptmp, rec->value, rec->value_len)); + } + } + else { + /* Absolute change. */ + + var->name = apr_pstrdup(msr->mp, var_name); + var->name_len = strlen(var->name); + var->value = apr_pstrdup(msr->mp, var_value); + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + apr_table_setn(target_col, var->name, (void *)var); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Set variable \"%s.%s\" to \"%s\".", + log_escape(mptmp, col_name), + log_escape_ex(mptmp, var->name, var->name_len), + log_escape_ex(mptmp, var->value, var->value_len)); + } + } + } + + /* Make note of the change so that we know later + * we need to persist the collection. + */ + apr_table_set(msr->collections_dirty, col_name, "1"); + + return 1; +} + +/* +* \brief Parse fuction for setvar input +* +* \param msr Pointer to the engine +* \param mptmp Pointer to the pool +* \param rule Pointer to rule struct +* \param action input data +* +* \retval -1 On failure +* \retval 0 On Collection failure +* \retval 1 On Success +*/ +static apr_status_t msre_action_setvar_parse(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + char *data = apr_pstrdup(mptmp, action->param); + char *var_name = NULL, *var_value = NULL; + char *s = NULL; + + /* Extract the name and the value. */ + /* IMP1 We have a function for this now, parse_name_eq_value? */ + s = strstr(data, "="); + if (s == NULL) { + var_name = data; + var_value = "1"; + } else { + var_name = data; + var_value = s + 1; + *s = '\0'; + + while ((*var_value != '\0')&&(isspace(*var_value))) var_value++; + } + + return msre_action_setvar_execute(msr,mptmp,rule,var_name,var_value); +} + + +/* expirevar */ +static apr_status_t msre_action_expirevar_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + char *data = apr_pstrdup(mptmp, action->param); + char *col_name = NULL, *var_name = NULL, *var_value = NULL; + char *s = NULL; + apr_table_t *target_col = NULL; + msc_string *var = NULL; + + /* Extract the name and the value. */ + /* IMP1 We have a function for this now, parse_name_eq_value? */ + s = strstr(data, "="); + if (s == NULL) { + var_name = data; + var_value = "1"; + } else { + var_name = data; + var_value = s + 1; + *s = '\0'; + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Expiring variable: %s=%s", var_name, var_value); + } + + /* Expand and escape any macros in the name */ + var = apr_palloc(msr->mp, sizeof(msc_string)); + if (var == NULL) { + msr_log(msr, 1, "Failed to allocate space to expand name macros"); + return -1; + } + var->value = var_name; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + var_name = log_escape_nq_ex(msr->mp, var->value, var->value_len); + + /* Choose the collection to work with. */ + s = strstr(var_name, "."); + if (s != NULL) { + col_name = var_name; + var_name = s + 1; + *s = '\0'; + + /* IMP1 No need to handle TX here because TX variables cannot expire, + * but we definitely need to have a better error message. + */ + + target_col = (apr_table_t *)apr_table_get(msr->collections, col_name); + if (target_col == NULL) { + msr_log(msr, 3, "Could not expire variable \"%s.%s\" as the collection does not exist.", + log_escape(msr->mp, col_name), log_escape(msr->mp, var_name)); + return 0; + } + } else { + msr_log(msr, 3, "Asked to expire variable \"%s\", but no collection name specified. ", + log_escape(msr->mp, var_name)); + return 0; + } + + /* To expire a variable we just place a special variable into + * the collection. Expiry actually happens when the collection + * is retrieved from storage the next time. + */ + var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = apr_psprintf(msr->mp, "__expire_%s", var_name); + var->name_len = strlen(var->name); + + /* Expand macros in value */ + var->value = var_value; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, msr->mp); + var_value = var->value; + + /* Calculate with the expanded value */ + var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(msr->request_time) + + atoi(var_value))); + var->value_len = strlen(var->value); + + apr_table_setn(target_col, var->name, (void *)var); + + msr_log(msr, 4, "Variable \"%s.%s\" set to expire in %s seconds.", col_name, + var_name, var_value); + + apr_table_set(msr->collections_dirty, col_name, "1"); + + return 1; +} + +/* deprecatevar */ +static apr_status_t msre_action_deprecatevar_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + char *data = apr_pstrdup(mptmp, action->param); + char *col_name = NULL, *var_name = NULL, *var_value = NULL; + char *s = NULL; + apr_table_t *target_col = NULL; + msc_string *var = NULL, *var_last_update_time = NULL; + apr_time_t last_update_time, current_time; + long current_value, new_value; + + /* Extract the name and the value. */ + /* IMP1 We have a function for this now, parse_name_eq_value? */ + s = strstr(data, "="); + if (s == NULL) { + var_name = data; + var_value = "1"; + } else { + var_name = data; + var_value = s + 1; + *s = '\0'; + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Deprecating variable: %s=%s", var_name, var_value); + } + + /* Expand and escape any macros in the name */ + var = apr_palloc(msr->mp, sizeof(msc_string)); + if (var == NULL) { + msr_log(msr, 1, "Failed to allocate space to expand name macros"); + return -1; + } + var->value = var_name; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + var_name = log_escape_nq_ex(msr->mp, var->value, var->value_len); + + /* Expand macros in value */ + var->value = var_value; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, msr->mp); + var_value = var->value; + + /* Choose the collection to work with. */ + s = strstr(var_name, "."); + if (s != NULL) { + col_name = var_name; + var_name = s + 1; + *s = '\0'; + + /* IMP1 Add message TX variables cannot deprecate in value. */ + + target_col = (apr_table_t *)apr_table_get(msr->collections, col_name); + if (target_col == NULL) { + msr_log(msr, 3, "Could not deprecate variable \"%s.%s\" as the collection does " + "not exist.", log_escape(msr->mp, col_name), log_escape(msr->mp, var_name)); + return 0; + } + } else { + msr_log(msr, 3, "Asked to deprecate variable \"%s\", but no collection name specified. ", + log_escape(msr->mp, var_name)); + return 0; + } + + /* Find the current value. */ + var = (msc_string *)apr_table_get(target_col, var_name); + if (var == NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Asked to deprecate variable \"%s.%s\", but it does not exist.", + log_escape(msr->mp, col_name), log_escape(msr->mp, var_name)); + } + return 0; + } + current_value = atoi(var->value); + + /* Find the last update time (of the collection). */ + var_last_update_time = (msc_string *)apr_table_get(target_col, "LAST_UPDATE_TIME"); + if (var_last_update_time == NULL) { + /* This is all right. If collection was created (and not restored from + * storage) then it won't have LAST_UPDATE_TIME - it was never updated. + */ + return 0; + } + + current_time = apr_time_sec(apr_time_now()); + last_update_time = atoi(var_last_update_time->value); + + s = strstr(var_value, "/"); + if (s == NULL) { + msr_log(msr, 3, "Incorrect format for the deprecatevar argument: \"%s\"", + log_escape(msr->mp, var_value)); + return 0; + } + *s = '\0'; + s++; + + /* Deprecate the value using the given speed and the + * time elapsed since the last update. + */ + new_value = current_value - + (atol(var_value) * ((current_time - last_update_time) / atol(s))); + if (new_value < 0) new_value = 0; + + /* Only change the value if it differs. */ + if (new_value != current_value) { + var->value = apr_psprintf(msr->mp, "%ld", new_value); + var->value_len = strlen(var->value); + + msr_log(msr, 4, "Deprecated variable \"%s.%s\" from %ld to %ld (%" APR_TIME_T_FMT " seconds since " + "last update).", log_escape(msr->mp, col_name), log_escape(msr->mp, var_name), + current_value, new_value, (apr_time_t)(current_time - last_update_time)); + + apr_table_set(msr->collections_dirty, col_name, "1"); + } else { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Not deprecating variable \"%s.%s\" because the new value (%ld) is " + "the same as the old one (%ld) (%" APR_TIME_T_FMT " seconds since last update).", + log_escape(msr->mp, col_name), log_escape(msr->mp, var_name), current_value, + new_value, (apr_time_t)(current_time - last_update_time)); + } + } + + return 1; +} + +static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, + const char *col_name, const char *col_key, unsigned int col_key_len) +{ + apr_table_t *table = NULL; + msc_string *var = NULL; + + /* IMP1 Cannot initialise the built-in collections this way. */ + + /* Does the collection exist already? */ + if (apr_table_get(msr->collections, col_name) != NULL) { + /* ENH Warn about this. */ + return 0; + } + + /* Init collection from storage. */ + table = collection_retrieve(msr, real_col_name, col_key, col_key_len); + + if (table == NULL) { + /* Does not exist yet - create new. */ + msr_log(msr, 4, "Creating collection (name \"%s\", key \"%s\").", + real_col_name, col_key); + + table = apr_table_make(msr->mp, 24); + if (table == NULL) return -1; + + /* IMP1 Is the timeout hard-coded to 3600? */ + + /* Add default timeout. */ + var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "__expire_KEY"; + var->name_len = strlen(var->name); + var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(msr->request_time) + 3600)); + var->value_len = strlen(var->value); + apr_table_setn(table, var->name, (void *)var); + + /* Remember the key. */ + var = apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "KEY"; + var->name_len = strlen(var->name); + var->value = apr_pstrmemdup(msr->mp, col_key, col_key_len); + var->value_len = col_key_len; + apr_table_setn(table, var->name, (void *)var); + + /* The timeout. */ + var = apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "TIMEOUT"; + var->name_len = strlen(var->name); + var->value = apr_psprintf(msr->mp, "%d", 3600); + var->value_len = strlen(var->value); + apr_table_setn(table, var->name, (void *)var); + + /* We may want to allow the user to unset KEY + * but we still need to preserve value to identify + * the collection in storage. + */ + + /* IMP1 Actually I want a better way to delete collections, + * perhaps a dedicated action. + */ + + var = apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "__key"; + var->name_len = strlen(var->name); + var->value = apr_pstrmemdup(msr->mp, col_key, col_key_len); + var->value_len = col_key_len; + apr_table_setn(table, var->name, (void *)var); + + /* Peristence code will need to know the name of the collection. */ + var = apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "__name"; + var->name_len = strlen(var->name); + var->value = apr_pstrdup(msr->mp, real_col_name); + var->value_len = strlen(var->value); + apr_table_setn(table, var->name, (void *)var); + + /* Create time. */ + var = apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "CREATE_TIME"; + var->name_len = strlen(var->name); + var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)apr_time_sec(msr->request_time)); + var->value_len = strlen(var->value); + apr_table_setn(table, var->name, (void *)var); + + /* Update counter. */ + var = apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "UPDATE_COUNTER"; + var->name_len = strlen(var->name); + var->value = "0"; + var->value_len = strlen(var->value); + apr_table_setn(table, var->name, (void *)var); + + /* This is a new collection. */ + var = apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "IS_NEW"; + var->name_len = strlen(var->name); + var->value = "1"; + var->value_len = strlen(var->value); + apr_table_setn(table, var->name, (void *)var); + } + + /* Record the original counter value before we change it */ + var = (msc_string *)apr_table_get(table, "UPDATE_COUNTER"); + if (var != NULL) { + collection_original_setvar(msr, col_name, var); + } + + /* Add the collection to the list. */ + apr_table_setn(msr->collections, apr_pstrdup(msr->mp, col_name), (void *)table); + + if (strcmp(col_name, real_col_name) != 0) { + msr_log(msr, 4, "Added collection \"%s\" to the list as \"%s\".", + log_escape(msr->mp, real_col_name), log_escape(msr->mp, col_name)); + } else { + msr_log(msr, 4, "Added collection \"%s\" to the list.", + log_escape(msr->mp, real_col_name)); + } + + return 1; +} + +/* initcol */ +static apr_status_t msre_action_initcol_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + char *data = apr_pstrdup(msr->mp, action->param); + char *col_name = NULL, *col_key = NULL; + unsigned int col_key_len; + + msc_string *var = NULL; + char *s = NULL; + + /* Extract the name and the value. */ + /* IMP1 We have a function for this now, parse_name_eq_value? */ + s = strstr(data, "="); + if (s == NULL) return 0; + col_name = data; + col_key = s + 1; + *s = '\0'; + + /* Expand the key and init collection from storage. */ + var = apr_pcalloc(mptmp, sizeof(msc_string)); + var->value = col_key; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + + col_key = var->value; + col_key_len = var->value_len; + + return init_collection(msr, col_name, col_name, col_key, col_key_len); +} + +/* setsid */ +static apr_status_t msre_action_setsid_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + msc_string *var = NULL; + char *real_col_name = NULL, *col_key = NULL; + unsigned int col_key_len; + + /* Construct session ID. */ + var = apr_pcalloc(mptmp, sizeof(msc_string)); + var->value = (char *)action->param; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + msr->sessionid = apr_pstrdup(msr->mp, var->value); + + /* Construct collection name. */ + col_key = var->value; + col_key_len = var->value_len; + real_col_name = apr_psprintf(mptmp, "%s_SESSION", msr->txcfg->webappid); + + /* Initialise collection. */ + return init_collection(msr, real_col_name, "SESSION", col_key, col_key_len); +} + +/* setuid */ +static apr_status_t msre_action_setuid_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + msc_string *var = NULL; + char *real_col_name = NULL, *col_key = NULL; + unsigned int col_key_len; + + /* Construct user ID. */ + var = apr_pcalloc(mptmp, sizeof(msc_string)); + var->value = (char *)action->param; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + msr->userid = apr_pstrdup(msr->mp, var->value); + + /* Construct collection name. */ + col_key = var->value; + col_key_len = var->value_len; + real_col_name = apr_psprintf(mptmp, "%s_USER", msr->txcfg->webappid); + + /* Initialise collection. */ + return init_collection(msr, real_col_name, "USER", col_key, col_key_len); +} + +/* exec */ +static char *msre_action_exec_validate(msre_engine *engine, msre_action *action) { + #if defined(WITH_LUA) + char *filename = (char *)action->param; + + /* TODO Support relative filenames. */ + + /* Process Lua scripts internally. */ + if (strlen(filename) > 4) { + char *p = filename + strlen(filename) - 4; + if ((p[0] == '.')&&(p[1] == 'l')&&(p[2] == 'u')&&(p[3] == 'a')) { + /* It's a Lua script. */ + msc_script *script = NULL; + + /* Compile script. */ + char *msg = lua_compile(&script, filename, engine->mp); + if (msg != NULL) return msg; + + action->param_data = script; + } + } + #endif + + return NULL; +} + +static apr_status_t msre_action_exec_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + #if defined(WITH_LUA) + if (action->param_data != NULL) { /* Lua */ + msc_script *script = (msc_script *)action->param_data; + char *my_error_msg = NULL; + + if (lua_execute(script, NULL, msr, rule, &my_error_msg) < 0) { + msr_log(msr, 1, "%s", my_error_msg); + return 0; + } + } else + #endif + { /* Execute as shell script. */ + char *script_output = NULL; + + int rc = apache2_exec(msr, action->param, NULL, &script_output); + if (rc != 1) { + msr_log(msr, 1, "Failed to execute: %s", action->param); + return 0; + } + } + + return 1; +} + +/* prepend */ +static apr_status_t msre_action_prepend_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + msc_string *var = NULL; + + /* Expand any macros in the text */ + var = apr_pcalloc(mptmp, sizeof(msc_string)); + if (var == NULL) return -1; + var->value = (char *)action->param; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + + /* ENH: Verify we really have to dup the data here. */ + msr->content_prepend = apr_pstrndup(msr->mp, var->value, var->value_len); + msr->content_prepend_len = var->value_len; + + return 1; +} + +/* append */ +static apr_status_t msre_action_append_execute(modsec_rec *msr, apr_pool_t *mptmp, + msre_rule *rule, msre_action *action) +{ + msc_string *var = NULL; + + /* Expand any macros in the text */ + var = apr_pcalloc(mptmp, sizeof(msc_string)); + if (var == NULL) return -1; + var->value = (char *)action->param; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + + /* ENH: Verify we really have to dup the data here. */ + msr->content_append = apr_pstrndup(msr->mp, var->value, var->value_len); + msr->content_append_len = var->value_len; + + return 1; +} + +/* -- */ + +/** + * + */ +void msre_engine_register_default_actions(msre_engine *engine) { + + /* id */ + msre_engine_action_register(engine, + "id", + ACTION_METADATA, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + NULL, + msre_action_id_init, + NULL + ); + + /* rev */ + msre_engine_action_register(engine, + "rev", + ACTION_METADATA, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + NULL, + msre_action_rev_init, + NULL + ); + + /* msg */ + msre_engine_action_register(engine, + "msg", + ACTION_METADATA, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + NULL, + msre_action_msg_init, + NULL + ); + + /* logdata */ + msre_engine_action_register(engine, + "logdata", + ACTION_METADATA, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + NULL, + msre_action_logdata_init, + NULL + ); + + /* severity */ + msre_engine_action_register(engine, + "severity", + ACTION_METADATA, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + NULL, + msre_action_severity_init, + NULL + ); + + /* chain */ + msre_engine_action_register(engine, + "chain", + ACTION_FLOW, + 0, 0, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + NULL, + msre_action_chain_init, + NULL + ); + + /* log */ + msre_engine_action_register(engine, + "log", + ACTION_NON_DISRUPTIVE, + 0, 0, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_LOG, + NULL, + msre_action_log_init, + NULL + ); + + /* nolog */ + msre_engine_action_register(engine, + "nolog", + ACTION_NON_DISRUPTIVE, + 0, 0, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_LOG, + NULL, + msre_action_nolog_init, + NULL + ); + + /* auditlog */ + msre_engine_action_register(engine, + "auditlog", + ACTION_NON_DISRUPTIVE, + 0, 0, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_AUDITLOG, + NULL, + msre_action_auditlog_init, + NULL + ); + + /* noauditlog */ + msre_engine_action_register(engine, + "noauditlog", + ACTION_NON_DISRUPTIVE, + 0, 0, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_AUDITLOG, + NULL, + msre_action_noauditlog_init, + NULL + ); + + /* deny */ + msre_engine_action_register(engine, + "block", + ACTION_DISRUPTIVE, + 0, 0, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_DISRUPTIVE, + NULL, + msre_action_block_init, + NULL + ); + + /* deny */ + msre_engine_action_register(engine, + "deny", + ACTION_DISRUPTIVE, + 0, 0, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_DISRUPTIVE, + NULL, + msre_action_deny_init, + NULL + ); + + /* status */ + msre_engine_action_register(engine, + "status", + ACTION_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + msre_action_status_validate, + msre_action_status_init, + NULL + ); + + /* drop */ + msre_engine_action_register(engine, + "drop", + ACTION_DISRUPTIVE, + 0, 0, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_DISRUPTIVE, + NULL, + msre_action_drop_init, + NULL + ); + + /* pause */ + msre_engine_action_register(engine, + "pause", + ACTION_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + msre_action_pause_validate, + msre_action_pause_init, + NULL + ); + + /* redirect */ + msre_engine_action_register(engine, + "redirect", + ACTION_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_DISRUPTIVE, + msre_action_redirect_validate, + msre_action_redirect_init, + msre_action_redirect_execute + ); + + /* proxy */ + msre_engine_action_register(engine, + "proxy", + ACTION_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_DISRUPTIVE, + msre_action_proxy_validate, + msre_action_proxy_init, + msre_action_proxy_execute + ); + + /* pass */ + msre_engine_action_register(engine, + "pass", + ACTION_DISRUPTIVE, + 0, 0, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_DISRUPTIVE, + NULL, + msre_action_pass_init, + NULL + ); + + /* skip */ + msre_engine_action_register(engine, + "skip", + ACTION_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_DISRUPTIVE, + msre_action_skip_validate, + msre_action_skip_init, + NULL + ); + + /* skipAfter */ + msre_engine_action_register(engine, + "skipAfter", + ACTION_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_DISRUPTIVE, + msre_action_skipAfter_validate, + msre_action_skipAfter_init, + NULL + ); + + /* allow */ + msre_engine_action_register(engine, + "allow", + ACTION_DISRUPTIVE, + 0, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_DISRUPTIVE, + msre_action_allow_validate, + msre_action_allow_init, + NULL + ); + + /* phase */ + /* ENH: This should be ACTION_NON_DISRUPTIVE or ACTION_FLOW??? */ + msre_engine_action_register(engine, + "phase", + ACTION_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + msre_action_phase_validate, + msre_action_phase_init, + NULL + ); + + /* t */ + msre_engine_action_register(engine, + "t", + ACTION_NON_DISRUPTIVE, + 1, 1, + ALLOW_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + msre_action_t_validate, + msre_action_t_init, + NULL + ); + + /* ctl */ + msre_engine_action_register(engine, + "ctl", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + msre_action_ctl_validate, + msre_action_ctl_init, + msre_action_ctl_execute + ); + + /* xmlns */ + msre_engine_action_register(engine, + "xmlns", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + msre_action_xmlns_validate, + NULL, + NULL + ); + + /* capture */ + msre_engine_action_register(engine, + "capture", + ACTION_NON_DISRUPTIVE, + 0, 0, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + NULL, + NULL, + NULL + ); + + /* sanitiseArg */ + msre_engine_action_register(engine, + "sanitiseArg", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_sanitiseArg_execute + ); + + /* sanitiseMatched */ + msre_engine_action_register(engine, + "sanitiseMatched", + ACTION_NON_DISRUPTIVE, + 0, 0, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_sanitiseMatched_execute + ); + + /* sanitiseRequestHeader */ + msre_engine_action_register(engine, + "sanitiseRequestHeader", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_sanitiseRequestHeader_execute + ); + + /* sanitiseResponseHeader */ + msre_engine_action_register(engine, + "sanitiseResponseHeader", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_sanitiseResponseHeader_execute + ); + + /* setenv */ + msre_engine_action_register(engine, + "setenv", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_setenv_execute + ); + + /* setvar */ + msre_engine_action_register(engine, + "setvar", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_setvar_parse + ); + + /* expirevar */ + msre_engine_action_register(engine, + "expirevar", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_expirevar_execute + ); + + /* deprecatevar */ + msre_engine_action_register(engine, + "deprecatevar", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_deprecatevar_execute + ); + + /* initcol */ + msre_engine_action_register(engine, + "initcol", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_initcol_execute + ); + + /* setsid */ + msre_engine_action_register(engine, + "setsid", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_setsid_execute + ); + + /* setuid */ + msre_engine_action_register(engine, + "setuid", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_setuid_execute + ); + + /* exec */ + msre_engine_action_register(engine, + "exec", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + msre_action_exec_validate, + NULL, + msre_action_exec_execute + ); + + /* multiMatch */ + msre_engine_action_register(engine, + "multiMatch", + ACTION_NON_DISRUPTIVE, + 0, 0, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + NULL, + NULL, + NULL + ); + + /* tag */ + /* ENH: This should be ACTION_METADATA??? */ + msre_engine_action_register(engine, + "tag", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_MANY, + ACTION_CGROUP_NONE, + NULL, + NULL, + NULL + ); + + /* prepend */ + msre_engine_action_register(engine, + "prepend", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_prepend_execute + ); + + /* append */ + msre_engine_action_register(engine, + "append", + ACTION_NON_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + ACTION_CGROUP_NONE, + NULL, + NULL, + msre_action_append_execute + ); +} diff --git a/2.5.13/2.5.x/apache2/re_operators.c b/2.5.13/2.5.x/apache2/re_operators.c new file mode 100644 index 00000000..ccc38655 --- /dev/null +++ b/2.5.13/2.5.x/apache2/re_operators.c @@ -0,0 +1,2205 @@ +/* + * 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 "re.h" +#include "msc_pcre.h" +#include "msc_geo.h" +#include "apr_lib.h" +#include "apr_strmatch.h" +#include "acmp.h" + +/** + * + */ +void msre_engine_op_register(msre_engine *engine, const char *name, + fn_op_param_init_t fn1, fn_op_execute_t fn2) +{ + msre_op_metadata *metadata = (msre_op_metadata *)apr_pcalloc(engine->mp, + sizeof(msre_op_metadata)); + if (metadata == NULL) return; + + metadata->name = name; + metadata->param_init = fn1; + metadata->execute = fn2; + apr_table_setn(engine->operators, name, (void *)metadata); +} + +/** + * + */ +msre_op_metadata *msre_engine_op_resolve(msre_engine *engine, const char *name) { + return (msre_op_metadata *)apr_table_get(engine->operators, name); +} + + + +/* -- Operators -- */ + +/* unconditionalMatch */ + +static int msre_op_unconditionalmatch_execute(modsec_rec *msr, msre_rule *rule, + msre_var *var, char **error_msg) +{ + *error_msg = "Unconditional match in SecAction."; + + /* Always match. */ + return 1; +} + +/* noMatch */ + +static int msre_op_nomatch_execute(modsec_rec *msr, msre_rule *rule, + msre_var *var, char **error_msg) +{ + *error_msg = "No match."; + + /* Never match. */ + return 0; +} + +/* rx */ + +static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) { + const char *errptr = NULL; + int erroffset; + msc_regex_t *regex; + const char *pattern = rule->op_param; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + /* Compile pattern */ + regex = msc_pregcomp_ex(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); + if (regex == NULL) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", + erroffset, errptr); + return 0; + } + + rule->op_param_data = regex; + + return 1; /* OK */ +} + +static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; + const char *target; + unsigned int target_length; + char *my_error_msg = NULL; + int ovector[33]; + int capture = 0; + int rc; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (regex == NULL) { + *error_msg = "Internal Error: regex data is null."; + return -1; + } + + /* If the given target is null run against an empty + * string. This is a behaviour consistent with previous + * releases. + */ + if (var->value == NULL) { + target = ""; + target_length = 0; + } else { + target = var->value; + target_length = var->value_len; + } + + /* Are we supposed to capture subexpressions? */ + capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; + + /* Show when the regex captures but "capture" is not set */ + if (msr->txcfg->debuglog_level >= 6) { + int capcount = 0; + rc = msc_fullinfo(regex, PCRE_INFO_CAPTURECOUNT, &capcount); + if ((capture == 0) && (capcount > 0)) { + msr_log(msr, 6, "Ignoring regex captures since \"capture\" action is not enabled."); + } + } + + /* We always use capture so that ovector can be used as working space + * and no memory has to be allocated for any backreferences. + */ + rc = msc_regexec_capture(regex, target, target_length, ovector, 30, &my_error_msg); + if ((rc == PCRE_ERROR_MATCHLIMIT) || (rc == PCRE_ERROR_RECURSIONLIMIT)) { + msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + + if (s == NULL) return -1; + s->name = apr_pstrdup(msr->mp, "MSC_PCRE_LIMITS_EXCEEDED"); + s->name_len = strlen(s->name); + s->value = apr_pstrdup(msr->mp, "1"); + s->value_len = 1; + if ((s->name == NULL)||(s->value == NULL)) return -1; + apr_table_setn(msr->tx_vars, s->name, (void *)s); + + *error_msg = apr_psprintf(msr->mp, + "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " + "Execution error - " + "PCRE limits exceeded (%d): %s", + rule,((rule->actionset != NULL)&&(rule->actionset->id != NULL)) ? rule->actionset->id : "-", + rule->filename != NULL ? rule->filename : "-", + rule->line_num,rc, my_error_msg); + + + msr_log(msr, 3, "%s.", *error_msg); + + return 0; /* No match. */ + } + else if (rc < -1) { + *error_msg = apr_psprintf(msr->mp, "Regex execution failed (%d): %s", + rc, my_error_msg); + return -1; + } + + /* Handle captured subexpressions. */ + if (capture && rc > 0) { + int i; + + /* Use the available captures. */ + for(i = 0; i < rc; i++) { + msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (s == NULL) return -1; + s->name = apr_psprintf(msr->mp, "%d", i); + s->name_len = strlen(s->name); + s->value = apr_pstrmemdup(msr->mp, + target + ovector[2*i], ovector[2*i + 1] - ovector[2*i]); + s->value_len = (ovector[2*i + 1] - ovector[2*i]); + if ((s->name == NULL)||(s->value == NULL)) return -1; + apr_table_setn(msr->tx_vars, s->name, (void *)s); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i, + log_escape_nq_ex(msr->mp, s->value, s->value_len)); + } + } + + /* Unset the remaining ones (from previous invocations). */ + for(i = rc; i <= 9; i++) { + char buf[24]; + apr_snprintf(buf, sizeof(buf), "%d", i); + apr_table_unset(msr->tx_vars, buf); + } + } + + if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ + /* We no longer escape the pattern here as it is done when logging */ + char *pattern = apr_pstrdup(msr->mp, regex->pattern); + + /* This message will be logged. */ + if (strlen(pattern) > 252) { + *error_msg = apr_psprintf(msr->mp, "Pattern match \"%.252s ...\" at %s.", + pattern, var->name); + } else { + *error_msg = apr_psprintf(msr->mp, "Pattern match \"%s\" at %s.", + pattern, var->name); + } + + return 1; + } + + /* No match. */ + return 0; +} + +/* pm */ + +static int msre_op_pm_param_init(msre_rule *rule, char **error_msg) { + ACMP *p; + const char *phrase; + const char *next; + + if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'pm'."); + return 0; /* ERROR */ + } + + p = acmp_create(0, rule->ruleset->mp); + if (p == NULL) return 0; + + phrase = apr_pstrdup(rule->ruleset->mp, rule->op_param); + + /* Loop through phrases */ + /* ENH: Need to allow quoted phrases w/space */ + for (;;) { + while((apr_isspace(*phrase) != 0) && (*phrase != '\0')) phrase++; + if (*phrase == '\0') break; + next = phrase; + while((apr_isspace(*next) == 0) && (*next != 0)) next++; + acmp_add_pattern(p, phrase, NULL, NULL, next - phrase); + phrase = next; + } + acmp_prepare(p); + rule->op_param_data = p; + return 1; +} + +/* pmFromFile */ + +static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) { + char errstr[1024]; + char buf[HUGE_STRING_LEN + 1]; + char *fn; + char *next; + char *start; + char *end; + const char *rulefile_path; + apr_status_t rc; + apr_file_t *fd; + ACMP *p; + + if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'pmFromFile'."); + return 0; /* ERROR */ + } + + p = acmp_create(0, rule->ruleset->mp); + if (p == NULL) return 0; + + fn = apr_pstrdup(rule->ruleset->mp, rule->op_param); + + /* Get the path of the rule filename to use as a base */ + rulefile_path = apr_pstrndup(rule->ruleset->mp, rule->filename, strlen(rule->filename) - strlen(apr_filepath_name_get(rule->filename))); + + #ifdef DEBUG_CONF + fprintf(stderr, "Rulefile path: \"%s\"\n", rulefile_path); + #endif + + /* Loop through filenames */ + /* ENH: Need to allow quoted filenames w/space */ + for (;;) { + const char *rootpath = NULL; + const char *filepath = NULL; + int line = 0; + + /* Trim whitespace */ + while((apr_isspace(*fn) != 0) && (*fn != '\0')) fn++; + if (*fn == '\0') break; + next = fn; + while((apr_isspace(*next) == 0) && (*next != '\0')) next++; + while((apr_isspace(*next) != 0) && (*next != '\0')) *(next++) = '\0'; + + /* Add path of the rule filename for a relative phrase filename */ + filepath = fn; + if (apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, rule->ruleset->mp) != APR_SUCCESS) { + /* We are not an absolute path. It could mean an error, but + * let that pass through to the open call for a better error */ + apr_filepath_merge(&fn, rulefile_path, fn, APR_FILEPATH_TRUENAME, rule->ruleset->mp); + } + + /* Open file and read */ + rc = apr_file_open(&fd, fn, APR_READ | APR_BUFFERED | APR_FILE_NOCLEANUP, 0, rule->ruleset->mp); + if (rc != APR_SUCCESS) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Could not open phrase file \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); + return 0; + } + + #ifdef DEBUG_CONF + fprintf(stderr, "Loading phrase file: \"%s\"\n", fn); + #endif + + /* Read one pattern per line skipping empty/commented */ + for(;;) { + line++; + rc = apr_file_gets(buf, HUGE_STRING_LEN, fd); + if (rc == APR_EOF) break; + if (rc != APR_SUCCESS) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Could not read \"%s\" line %d: %s", fn, line, apr_strerror(rc, errstr, 1024)); + return 0; + } + + /* Trim Whitespace */ + start = buf; + while ((apr_isspace(*start) != 0) && (*start != '\0')) start++; + end = buf + strlen(buf); + if (end > start) end--; + while ((end > start) && (apr_isspace(*end) != 0)) end--; + if (end > start) { + *(++end) = '\0'; + } + + /* Ignore empty lines and comments */ + if ((start == end) || (*start == '#')) continue; + + #ifdef DEBUG_CONF + fprintf(stderr, "Adding phrase file pattern: \"%s\"\n", buf); + #endif + + acmp_add_pattern(p, start, NULL, NULL, (end - start)); + } + fn = next; + } + if (fd != NULL) apr_file_close(fd); + acmp_prepare(p); + rule->op_param_data = p; + return 1; +} + +static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + const char *match = NULL; + apr_status_t rc = 0; + int capture; + ACMPT pt; + + /* Nothing to read */ + if ((var->value == NULL) || (var->value_len == 0)) return 0; + + /* Are we supposed to capture subexpressions? */ + capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; + + pt.parser = (ACMP *)rule->op_param_data; + pt.ptr = NULL; + + rc = acmp_process_quick(&pt, &match, var->value, var->value_len); + if (rc) { + char *match_escaped = log_escape(msr->mp, match ? match : ""); + + /* This message will be logged. */ + if (strlen(match_escaped) > 252) { + *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%.252s ...\" at %s.", + match_escaped, var->name); + } else { + *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%s\" at %s.", + match_escaped, var->name); + } + + /* Handle capture as tx.0=match */ + if (capture) { + int i; + msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + + if (s == NULL) return -1; + + s->name = "0"; + s->name_len = strlen(s->name); + s->value = apr_pstrdup(msr->mp, match); + if (s->value == NULL) return -1; + s->value_len = strlen(s->value); + apr_table_setn(msr->tx_vars, s->name, (void *)s); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Added phrase match to TX.0: %s", + log_escape_nq_ex(msr->mp, s->value, s->value_len)); + } + + /* Unset the remaining ones (from previous invocations). */ + for(i = rc; i <= 9; i++) { + char buf[2]; + apr_snprintf(buf, sizeof(buf), "%d", i); + apr_table_unset(msr->tx_vars, buf); + } + } + + return 1; + } + return rc; +} + +/* within */ + +static int msre_op_within_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; + const char *target; + unsigned int match_length; + unsigned int target_length = 0; + unsigned int i, i_max; + + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (str->value == NULL) { + *error_msg = "Internal Error: match string is null."; + return -1; + } + + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + + /* If the given target is null we give up without a match */ + if (var->value == NULL) { + /* No match. */ + return 0; + } + + target = var->value; + target_length = var->value_len; + + /* The empty string always matches */ + if (target_length == 0) { + /* Match. */ + *error_msg = apr_psprintf(msr->mp, "String match within \"\" at %s.", + var->name); + return 1; + } + + /* This is impossible to match */ + if (target_length > match_length) { + /* No match. */ + return 0; + } + + /* scan for first character, then compare from there until we + * have a match or there is no room left in the target + */ + i_max = match_length - target_length; + for (i = 0; i <= i_max; i++) { + if (match[i] == target[0]) { + if (memcmp((target + 1), (match + i + 1), (target_length - 1)) == 0) { + /* match. */ + *error_msg = apr_psprintf(msr->mp, "String match within \"%s\" at %s.", + log_escape_ex(msr->mp, match, match_length), + var->name); + return 1; + } + } + } + + /* No match. */ + return 0; +} + +/* contains */ + +static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; + const char *target; + unsigned int match_length; + unsigned int target_length = 0; + unsigned int i, i_max; + + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (str->value == NULL) { + *error_msg = "Internal Error: match string is null."; + return -1; + } + + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + + /* If the given target is null run against an empty + * string. This is a behaviour consistent with previous + * releases. + */ + if (var->value == NULL) { + target = ""; + target_length = 0; + } else { + target = var->value; + target_length = var->value_len; + } + + /* The empty string always matches */ + if (match_length == 0) { + /* Match. */ + *error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name); + return 1; + } + + /* This is impossible to match */ + if (match_length > target_length) { + /* No match. */ + return 0; + } + + /* scan for first character, then compare from there until we + * have a match or there is no room left in the target + */ + i_max = target_length - match_length; + for (i = 0; i <= i_max; i++) { + /* First character matched - avoid func call */ + if (target[i] == match[0]) { + /* See if remaining matches */ + if ( (match_length == 1) + || (memcmp((match + 1), (target + i + 1), (match_length - 1)) == 0)) + { + /* Match. */ + *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", + log_escape_ex(msr->mp, match, match_length), + var->name); + return 1; + } + } + } + + /* No match. */ + return 0; +} + +/* containsWord */ + +static int msre_op_containsWord_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; + const char *target; + unsigned int match_length; + unsigned int target_length = 0; + unsigned int i, i_max; + int rc = 0; + + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (str->value == NULL) { + *error_msg = "Internal Error: match string is null."; + return -1; + } + + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + + /* If the given target is null run against an empty + * string. This is a behaviour consistent with previous + * releases. + */ + if (var->value == NULL) { + target = ""; + target_length = 0; + } else { + target = var->value; + target_length = var->value_len; + } + + /* The empty string always matches */ + if (match_length == 0) { + /* Match. */ + *error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name); + return 1; + } + + /* This is impossible to match */ + if (match_length > target_length) { + /* No match. */ + return 0; + } + + /* scan for first character, then compare from there until we + * have a match or there is no room left in the target + */ + i_max = target_length - match_length; + for (i = 0; i <= i_max; i++) { + + /* Previous char must have been a start or non-word */ + if ((i > 0) && (apr_isalnum(target[i-1])||(target[i-1] == '_'))) + continue; + + /* First character matched - avoid func call */ + if (target[i] == match[0]) { + /* See if remaining matches */ + if ( (match_length == 1) + || (memcmp((match + 1), (target + i + 1), (match_length - 1)) == 0)) + { + /* check boundaries */ + if (i == i_max) { + /* exact/end word match */ + rc = 1; + } + else if (!(apr_isalnum(target[i + match_length])||(target[i + match_length] == '_'))) { + /* start/mid word match */ + rc = 1; + } + } + } + } + + if (rc == 1) { + /* Maybe a match. */ + *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", + log_escape_ex(msr->mp, match, match_length), + var->name); + return 1; + } + + /* No match. */ + *error_msg = NULL; + return 0; +} + +/* streq */ + +static int msre_op_streq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; + const char *target; + unsigned int match_length; + unsigned int target_length; + + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (str->value == NULL) { + *error_msg = "Internal Error: match string is null."; + return -1; + } + + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + + /* If the given target is null run against an empty + * string. This is a behaviour consistent with previous + * releases. + */ + if (var->value == NULL) { + target = ""; + target_length = 0; + } else { + target = var->value; + target_length = var->value_len; + } + + /* These are impossible to match */ + if (match_length != target_length) { + /* No match. */ + return 0; + } + + if (memcmp(match, target, target_length) == 0) { + /* Match. */ + *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", + log_escape_ex(msr->mp, match, match_length), + var->name); + return 1; + } + + /* No match. */ + return 0; +} + +/* beginsWith */ + +static int msre_op_beginsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; + const char *target; + unsigned int match_length; + unsigned int target_length; + + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (str->value == NULL) { + *error_msg = "Internal Error: match string is null."; + return -1; + } + + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + + /* If the given target is null run against an empty + * string. This is a behaviour consistent with previous + * releases. + */ + if (var->value == NULL) { + target = ""; + target_length = 0; + } else { + target = var->value; + target_length = var->value_len; + } + + /* The empty string always matches */ + if (match_length == 0) { + /* Match. */ + *error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name); + return 1; + } + + /* This is impossible to match */ + if (match_length > target_length) { + /* No match. */ + return 0; + } + + if (memcmp(match, target, match_length) == 0) { + /* Match. */ + *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", + log_escape_ex(msr->mp, match, match_length), + var->name); + return 1; + } + + /* No match. */ + return 0; +} + +/* endsWith */ + +static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; + const char *target; + unsigned int match_length; + unsigned int target_length; + + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (str->value == NULL) { + *error_msg = "Internal Error: match string is null."; + return -1; + } + + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + + /* If the given target is null run against an empty + * string. This is a behaviour consistent with previous + * releases. + */ + if (var->value == NULL) { + target = ""; + target_length = 0; + } else { + target = var->value; + target_length = var->value_len; + } + + /* The empty string always matches */ + if (match_length == 0) { + /* Match. */ + *error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name); + return 1; + } + + /* This is impossible to match */ + if (match_length > target_length) { + /* No match. */ + return 0; + } + + if (memcmp(match, (target + (target_length - match_length)), match_length) == 0) { + /* Match. */ + *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", + log_escape_ex(msr->mp, match, match_length), + var->name); + return 1; + } + + /* No match. */ + return 0; +} + +/* m */ + +static int msre_op_m_param_init(msre_rule *rule, char **error_msg) { + const apr_strmatch_pattern *compiled_pattern; + const char *pattern = rule->op_param; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + /* Compile pattern */ + compiled_pattern = apr_strmatch_precompile(rule->ruleset->mp, pattern, 1); + if (compiled_pattern == NULL) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern: %s", pattern); + return 0; + } + + rule->op_param_data = (void *)compiled_pattern; + + return 1; /* OK */ +} + +static int msre_op_m_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + apr_strmatch_pattern *compiled_pattern = (apr_strmatch_pattern *)rule->op_param_data; + const char *target; + unsigned int target_length; + const char *rc; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (compiled_pattern == NULL) { + *error_msg = "Internal Error: strnmatch data is null."; + return -1; + } + + /* If the given target is null run against an empty + * string. This is a behaviour consistent with previous + * releases. + */ + if (var->value == NULL) { + target = ""; + target_length = 0; + } else { + target = var->value; + target_length = var->value_len; + } + + rc = apr_strmatch(compiled_pattern, target, target_length); + if (rc == NULL) { + /* No match. */ + return 0; + } + + *error_msg = apr_psprintf(msr->mp, "Pattern match \"%s\" at %s.", + log_escape(msr->mp, rule->op_param), var->name); + + /* Match. */ + return 1; +} + +/* validateDTD */ + +static int msre_op_validateDTD_init(msre_rule *rule, char **error_msg) { + /* ENH Verify here the file actually exists. */ + return 1; +} + +static int msre_op_validateDTD_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, + char **error_msg) +{ + xmlValidCtxtPtr cvp; + xmlDtdPtr dtd; + + if ((msr->xml == NULL)||(msr->xml->doc == NULL)) { + *error_msg = apr_psprintf(msr->mp, + "XML document tree could not be found for DTD validation."); + return -1; + } + + if (msr->xml->well_formed != 1) { + *error_msg = apr_psprintf(msr->mp, + "XML: DTD validation failed because content is not well formed."); + return 1; + } + + /* Make sure there were no other generic processing errors */ + if (msr->msc_reqbody_error) { + *error_msg = apr_psprintf(msr->mp, + "XML: DTD validation could not proceed due to previous" + " processing errors."); + return 1; + } + + dtd = xmlParseDTD(NULL, (const xmlChar *)rule->op_param); /* EHN support relative filenames */ + if (dtd == NULL) { + *error_msg = apr_psprintf(msr->mp, "XML: Failed to load DTD: %s", rule->op_param); + return -1; + } + + cvp = xmlNewValidCtxt(); + if (cvp == NULL) { + *error_msg = "XML: Failed to create a validation context."; + xmlFreeDtd(dtd); + return -1; + } + + /* Send validator errors/warnings to msr_log */ + /* NOTE: No xmlDtdSetValidErrors()? */ + cvp->error = (xmlSchemaValidityErrorFunc)msr_log_error; + cvp->warning = (xmlSchemaValidityErrorFunc)msr_log_warn; + cvp->userData = msr; + + if (!xmlValidateDtd(cvp, msr->xml->doc, dtd)) { + *error_msg = "XML: DTD validation failed."; + xmlFreeValidCtxt(cvp); + xmlFreeDtd(dtd); + return 1; /* No match. */ + } + + msr_log(msr, 4, "XML: Successfully validated payload against DTD: %s", rule->op_param); + + xmlFreeValidCtxt(cvp); + xmlFreeDtd(dtd); + + /* Match. */ + return 0; +} + +/* validateSchema */ + +static int msre_op_validateSchema_init(msre_rule *rule, char **error_msg) { + /* ENH Verify here the file actually exists. */ + return 1; +} + +static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, + char **error_msg) +{ + xmlSchemaParserCtxtPtr parserCtx; + xmlSchemaValidCtxtPtr validCtx; + xmlSchemaPtr schema; + int rc; + + if ((msr->xml == NULL)||(msr->xml->doc == NULL)) { + *error_msg = apr_psprintf(msr->mp, + "XML document tree could not be found for schema validation."); + return -1; + } + + if (msr->xml->well_formed != 1) { + *error_msg = apr_psprintf(msr->mp, + "XML: Schema validation failed because content is not well formed."); + return 1; + } + + /* Make sure there were no other generic processing errors */ + if (msr->msc_reqbody_error) { + *error_msg = apr_psprintf(msr->mp, + "XML: Schema validation could not proceed due to previous" + " processing errors."); + return 1; + } + + parserCtx = xmlSchemaNewParserCtxt(rule->op_param); /* ENH support relative filenames */ + if (parserCtx == NULL) { + *error_msg = apr_psprintf(msr->mp, "XML: Failed to load Schema from file: %s", + rule->op_param); + return -1; + } + + /* Send parser errors/warnings to msr_log */ + xmlSchemaSetParserErrors(parserCtx, (xmlSchemaValidityErrorFunc)msr_log_error, (xmlSchemaValidityWarningFunc)msr_log_warn, msr); + + schema = xmlSchemaParse(parserCtx); + if (schema == NULL) { + *error_msg = apr_psprintf(msr->mp, "XML: Failed to load Schema: %s", rule->op_param); + xmlSchemaFreeParserCtxt(parserCtx); + return -1; + } + + validCtx = xmlSchemaNewValidCtxt(schema); + if (validCtx == NULL) { + *error_msg = "XML: Failed to create validation context."; + xmlSchemaFree(schema); + xmlSchemaFreeParserCtxt(parserCtx); + return -1; + } + + /* Send validator errors/warnings to msr_log */ + xmlSchemaSetValidErrors(validCtx, (xmlSchemaValidityErrorFunc)msr_log_error, (xmlSchemaValidityWarningFunc)msr_log_warn, msr); + + rc = xmlSchemaValidateDoc(validCtx, msr->xml->doc); + if (rc != 0) { + *error_msg = "XML: Schema validation failed."; + xmlSchemaFree(schema); + xmlSchemaFreeParserCtxt(parserCtx); + return 1; /* No match. */ + } + + msr_log(msr, 4, "XML: Successfully validated payload against Schema: %s", rule->op_param); + + xmlSchemaFree(schema); + xmlSchemaFreeValidCtxt(validCtx); + + return 0; +} + +/* verifyCC */ + +/** + * Luhn Mod-10 Method (ISO 2894/ANSI 4.13) + */ +static int luhn_verify(const char *ccnumber, int len) { + int sum[2] = { 0, 0 }; + int odd = 0; + int digits = 0; + int i; + + /* Weighted lookup table which is just a precalculated (i = index): + * i*2 + (( (i*2) > 9 ) ? -9 : 0) + */ + static int wtable[10] = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9}; /* weight lookup table */ + + /* Add up only digits (weighted digits via lookup table) + * for both odd and even CC numbers to avoid 2 passes. + */ + for (i = 0; i < len; i++) { + if (apr_isdigit(ccnumber[i])) { + sum[0] += (!odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0')); + sum[1] += (odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0')); + odd = 1 - odd; /* alternate weights */ + digits++; + } + } + + /* No digits extracted */ + if (digits == 0) return 0; + + /* Do a mod 10 on the sum */ + sum[odd] %= 10; + + /* If the result is a zero the card is valid. */ + return sum[odd] ? 0 : 1; +} + +static int msre_op_verifyCC_init(msre_rule *rule, char **error_msg) { + const char *errptr = NULL; + int erroffset; + msc_regex_t *regex; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + /* Compile rule->op_param */ + regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); + if (regex == NULL) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", + erroffset, errptr); + return 0; + } + + rule->op_param_data = regex; + + return 1; /* OK */ +} + +static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; + const char *target; + unsigned int target_length; + char *my_error_msg = NULL; + int ovector[33]; + int rc; + int is_cc = 0; + int offset; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (regex == NULL) { + *error_msg = "Internal Error: regex data is null."; + return -1; + } + + memset(ovector, 0, sizeof(ovector)); + + /* If the given target is null run against an empty + * string. This is a behaviour consistent with previous + * releases. + */ + if (var->value == NULL) { + target = ""; + target_length = 0; + } else { + target = var->value; + target_length = var->value_len; + } + + for (offset = 0; ((unsigned int)offset < target_length) && (is_cc == 0); offset++) { + if (msr->txcfg->debuglog_level >= 9) { + if (offset > 0) { + msr_log(msr, 9, "Continuing CC# search at target offset %d.", offset); + } + } + + rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg); + + /* If there was no match, then we are done. */ + if (rc == PCRE_ERROR_NOMATCH) { + break; + } + + if (rc < -1) { + *error_msg = apr_psprintf(msr->mp, "CC# regex execution failed: %s", my_error_msg); + return -1; + } + + /* Verify a match. */ + if (rc > 0) { + const char *match = target + ovector[0]; + int length = ovector[1] - ovector[0]; + int i = 0; + + offset = ovector[2*i]; + + /* Check the Luhn using the match string */ + is_cc = luhn_verify(match, length); + + /* Not a CC number, then try another match where we left off. */ + if (!is_cc) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CC# Luhn check failed at target offset %d: \"%.*s\"", offset, length, match); + } + + continue; + } + + /* We have a potential CC number and need to set any captures + * and we are done. + */ + + if (apr_table_get(rule->actionset->actions, "capture")) { + for(; i < rc; i++) { + msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (s == NULL) return -1; + s->name = apr_psprintf(msr->mp, "%d", i); + s->name_len = strlen(s->name); + s->value = apr_pstrmemdup(msr->mp, match, length); + s->value_len = length; + if ((s->name == NULL)||(s->value == NULL)) return -1; + + apr_table_setn(msr->tx_vars, s->name, (void *)s); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i, + log_escape_nq_ex(msr->mp, s->value, s->value_len)); + } + } + } + + /* Unset the remaining TX vars (from previous invocations). */ + for(; i <= 9; i++) { + char buf[24]; + apr_snprintf(buf, sizeof(buf), "%i", i); + apr_table_unset(msr->tx_vars, buf); + } + + break; + } + } + + if (is_cc) { + /* Match. */ + + /* This message will be logged. */ + *error_msg = apr_psprintf(msr->mp, "CC# match \"%s\" at %s. [offset \"%d\"]", + regex->pattern, var->name, offset); + + return 1; + } + + /* No match. */ + return 0; +} + + +/** + * Perform geograpical lookups on an IP/Host. + */ +static int msre_op_geoLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, + char **error_msg) +{ + geo_rec rec; + geo_db *geo = msr->txcfg->geo; + const char *geo_host = var->value; + msc_string *s = NULL; + int rc; + + *error_msg = NULL; + + if (geo == NULL) { + msr_log(msr, 1, "Geo lookup for \"%s\" attempted without a database. Set SecGeoLookupDB.", log_escape(msr->mp, geo_host)); + return 0; + } + + + rc = geo_lookup(msr, &rec, geo_host, error_msg); + if (rc <= 0) { + if (! *error_msg) { + *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed at %s.", log_escape_nq(msr->mp, geo_host), var->name); + } + apr_table_clear(msr->geo_vars); + return rc; + } + if (! *error_msg) { + *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" succeeded at %s.", + log_escape_nq(msr->mp, geo_host), var->name); + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "GEO: %s={country_code=%s, country_code3=%s, country_name=%s, country_continent=%s, region=%s, city=%s, postal_code=%s, latitude=%f, longitude=%f, dma_code=%d, area_code=%d}", + geo_host, + rec.country_code, + rec.country_code3, + rec.country_name, + rec.country_continent, + rec.region, + rec.city, + rec.postal_code, + rec.latitude, + rec.longitude, + rec.dma_code, + rec.area_code); + } + + s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + s->name = apr_pstrdup(msr->mp, "COUNTRY_CODE"); + s->name_len = strlen(s->name); + s->value = apr_pstrdup(msr->mp, rec.country_code ? rec.country_code : ""); + s->value_len = strlen(s->value); + apr_table_setn(msr->geo_vars, s->name, (void *)s); + + s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + s->name = apr_pstrdup(msr->mp, "COUNTRY_CODE3"); + s->name_len = strlen(s->name); + s->value = apr_pstrdup(msr->mp, rec.country_code3 ? rec.country_code3 : ""); + s->value_len = strlen(s->value); + apr_table_setn(msr->geo_vars, s->name, (void *)s); + + s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + s->name = apr_pstrdup(msr->mp, "COUNTRY_NAME"); + s->name_len = strlen(s->name); + s->value = apr_pstrdup(msr->mp, rec.country_name ? rec.country_name : ""); + s->value_len = strlen(s->value); + apr_table_setn(msr->geo_vars, s->name, (void *)s); + + s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + s->name = apr_pstrdup(msr->mp, "COUNTRY_CONTINENT"); + s->name_len = strlen(s->name); + s->value = apr_pstrdup(msr->mp, rec.country_continent ? rec.country_continent : ""); + s->value_len = strlen(s->value); + apr_table_setn(msr->geo_vars, s->name, (void *)s); + + s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + s->name = apr_pstrdup(msr->mp, "REGION"); + s->name_len = strlen(s->name); + s->value = apr_pstrdup(msr->mp, rec.region ? rec.region : ""); + s->value_len = strlen(s->value); + apr_table_setn(msr->geo_vars, s->name, (void *)s); + + s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + s->name = apr_pstrdup(msr->mp, "CITY"); + s->name_len = strlen(s->name); + s->value = apr_pstrdup(msr->mp, rec.city ? rec.city : ""); + s->value_len = strlen(s->value); + apr_table_setn(msr->geo_vars, s->name, (void *)s); + + s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + s->name = apr_pstrdup(msr->mp, "POSTAL_CODE"); + s->name_len = strlen(s->name); + s->value = apr_pstrdup(msr->mp, rec.postal_code ? rec.postal_code : ""); + s->value_len = strlen(s->value); + apr_table_setn(msr->geo_vars, s->name, (void *)s); + + s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + s->name = apr_pstrdup(msr->mp, "LATITUDE"); + s->name_len = strlen(s->name); + s->value = apr_psprintf(msr->mp, "%f", rec.latitude); + s->value_len = strlen(s->value); + apr_table_setn(msr->geo_vars, s->name, (void *)s); + + s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + s->name = apr_pstrdup(msr->mp, "LONGITUDE"); + s->name_len = strlen(s->name); + s->value = apr_psprintf(msr->mp, "%f", rec.longitude); + s->value_len = strlen(s->value); + apr_table_setn(msr->geo_vars, s->name, (void *)s); + + s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + s->name = apr_pstrdup(msr->mp, "DMA_CODE"); + s->name_len = strlen(s->name); + s->value = apr_psprintf(msr->mp, "%d", rec.dma_code); + s->value_len = strlen(s->value); + apr_table_setn(msr->geo_vars, s->name, (void *)s); + + s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + s->name = apr_pstrdup(msr->mp, "AREA_CODE"); + s->name_len = strlen(s->name); + s->value = apr_psprintf(msr->mp, "%d", rec.area_code); + s->value_len = strlen(s->value); + apr_table_setn(msr->geo_vars, s->name, (void *)s); + + return 1; +} + +/* rbl */ + +static int msre_op_rbl_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + unsigned int h0, h1, h2, h3; + char *name_to_check = NULL; + char *target = NULL; + apr_sockaddr_t *sa = NULL; + apr_status_t rc; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + /* ENH Add IPv6 support. */ + + target = apr_pstrmemdup(msr->mp, var->value, var->value_len); + if (target == NULL) return -1; + + /* Construct the host name we want to resolve. */ + if (sscanf(target, "%d.%d.%d.%d", &h0, &h1, &h2, &h3) == 4) { + /* IPv4 address */ + name_to_check = apr_psprintf(msr->mp, "%d.%d.%d.%d.%s", h3, h2, h1, h0, rule->op_param); + } else { + /* Assume the input is a domain name. */ + name_to_check = apr_psprintf(msr->mp, "%s.%s", target, rule->op_param); + } + + if (name_to_check == NULL) return -1; + + rc = apr_sockaddr_info_get(&sa, name_to_check, + APR_UNSPEC/*msr->r->connection->remote_addr->family*/, 0, 0, msr->mp); + if (rc == APR_SUCCESS) { + *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s.", + log_escape_nq(msr->mp, name_to_check), var->name); + return 1; /* Match. */ + } + + msr_log(msr, 5, "RBL lookup of %s failed at %s.", log_escape_nq(msr->mp, name_to_check), var->name); + + /* No match. */ + return 0; +} + +/* inspectFile */ + +static int msre_op_inspectFile_init(msre_rule *rule, char **error_msg) { + char *filename = (char *)rule->op_param; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if ((filename == NULL)||(is_empty_string(filename))) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Operator @inspectFile requires parameter."); + return -1; + } + + filename = resolve_relative_path(rule->ruleset->mp, rule->filename, filename); + + #if defined(WITH_LUA) + /* ENH Write & use string_ends(s, e). */ + if (strlen(rule->op_param) > 4) { + char *p = filename + strlen(filename) - 4; + if ((p[0] == '.')&&(p[1] == 'l')&&(p[2] == 'u')&&(p[3] == 'a')) + { + msc_script *script = NULL; + + /* Compile script. */ + *error_msg = lua_compile(&script, filename, rule->ruleset->mp); + if (*error_msg != NULL) return -1; + + rule->op_param_data = script; + } + } + #endif + + if (rule->op_param_data == NULL) { + /* ENH Verify the script exists and that we have + * the rights to execute it. + */ + } + + return 1; +} + +static int msre_op_inspectFile_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, + char **error_msg) +{ + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (rule->op_param_data == NULL) { + /* Execute externally, as native binary/shell script. */ + char *script_output = NULL; + char const *argv[5]; + const char *approver_script = rule->op_param; + const char *target_file = apr_pstrmemdup(msr->mp, var->value, var->value_len); + + msr_log(msr, 4, "Executing %s to inspect %s.", approver_script, target_file); + + argv[0] = approver_script; + argv[1] = target_file; + argv[2] = NULL; + + if (apache2_exec(msr, approver_script, (const char **)argv, &script_output) <= 0) { + *error_msg = apr_psprintf(msr->mp, "Execution of the approver script \"%s\" failed (invocation failed).", + log_escape(msr->mp, approver_script)); + return -1; + } + + if (script_output == NULL) { + *error_msg = apr_psprintf(msr->mp, "Execution of the approver script \"%s\" failed (no output).", + log_escape(msr->mp, approver_script)); + return -1; + } + + if (script_output[0] != '1') { + *error_msg = apr_psprintf(msr->mp, "File \"%s\" rejected by the approver script \"%s\": %s", + log_escape(msr->mp, target_file), log_escape(msr->mp, approver_script), + log_escape_nq(msr->mp, script_output)); + return 1; /* Match. */ + } + } + #if defined(WITH_LUA) + else { + /* Execute internally, as Lua script. */ + char *target = apr_pstrmemdup(msr->mp, var->value, var->value_len); + msc_script *script = (msc_script *)rule->op_param_data; + int rc; + + rc = lua_execute(script, target, msr, rule, error_msg); + if (rc < 0) { + /* Error. */ + return -1; + } + + return rc; + } + #endif + + /* No match. */ + return 0; +} + +/* validateByteRange */ + +static int msre_op_validateByteRange_init(msre_rule *rule, char **error_msg) { + char *p = NULL, *saveptr = NULL; + char *table = NULL, *data = NULL; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (rule->op_param == NULL) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for validateByteRange."); + return -1; + } + + /* Initialise. */ + data = apr_pstrdup(rule->ruleset->mp, rule->op_param); + rule->op_param_data = apr_pcalloc(rule->ruleset->mp, 32); + if ((data == NULL)||(rule->op_param_data == NULL)) return -1; + table = rule->op_param_data; + + /* Extract parameters and update table. */ + p = apr_strtok(data, ",", &saveptr); + while(p != NULL) { + char *s = strstr(p, "-"); + if (s == NULL) { + /* Single value. */ + int x = atoi(p); + if ((x < 0)||(x > 255)) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range value: %d", x); + return 0; + } + table[x>>3] = (table[x>>3] | (1 << (x & 0x7))); + } else { + /* Range. */ + int start = atoi(p); + int end = atoi(s + 1); + + if ((start < 0)||(start > 255)) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range start value: %d", + start); + return 0; + } + if ((end < 0)||(end > 255)) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range end value: %d", end); + return 0; + } + if (start > end) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range: %d-%d", start, end); + return 0; + } + + while(start <= end) { + table[start >> 3] = (table[start >> 3] | (1 << (start & 0x7))); + start++; + } + } + + p = apr_strtok(NULL, ",", &saveptr); + } + + return 1; +} + +static int msre_op_validateByteRange_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, + char **error_msg) +{ + char *table = rule->op_param_data; + unsigned int i, count; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (table == NULL) { + *error_msg = apr_psprintf(msr->mp, "Internal Error: validateByteRange table not " + "initialised."); + return -1; + } + + /* Check every byte of the target to detect characters that are not allowed. */ + + count = 0; + for(i = 0; i < var->value_len; i++) { + int x = ((unsigned char *)var->value)[i]; + if (!(table[x >> 3] & (1 << (x & 0x7)))) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Value %d in %s outside range: %s", x, var->name, rule->op_param); + } + count++; + } + } + + if (count == 0) return 0; /* Valid - no match. */ + + *error_msg = apr_psprintf(msr->mp, "Found %d byte(s) in %s outside range: %s.", + count, var->name, rule->op_param); + + return 1; /* Invalid - match.*/ +} + +/* validateUrlEncoding */ + +static int validate_url_encoding(const char *input, long int input_length) { + int i; + + if ((input == NULL)||(input_length < 0)) return -1; + + i = 0; + while (i < input_length) { + if (input[i] == '%') { + if (i + 2 >= input_length) { + /* Not enough bytes. */ + return -3; + } + else { + /* Here we only decode a %xx combination if it is valid, + * leaving it as is otherwise. + */ + char c1 = input[i + 1]; + char c2 = input[i + 2]; + + if ( (((c1 >= '0')&&(c1 <= '9')) || ((c1 >= 'a')&&(c1 <= 'f')) || ((c1 >= 'A')&&(c1 <= 'F'))) + && (((c2 >= '0')&&(c2 <= '9')) || ((c2 >= 'a')&&(c2 <= 'f')) || ((c2 >= 'A')&&(c2 <= 'F'))) ) + { + i += 3; + } else { + /* Non-hexadecimal characters used in encoding. */ + return -2; + } + } + } else { + i++; + } + } + + return 1; +} + +static int msre_op_validateUrlEncoding_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, + char **error_msg) +{ + int rc = validate_url_encoding(var->value, var->value_len); + switch(rc) { + case 1 : + /* Encoding is valid */ + *error_msg = apr_psprintf(msr->mp, "Valid URL Encoding at %s.", var->name); + break; + case -2 : + *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Non-hexadecimal " + "digits used at %s.", var->name); + return 1; /* Invalid match. */ + break; + case -3 : + *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Not enough characters " + "at the end of input at %s.", var->name); + return 1; /* Invalid match. */ + break; + case -1 : + default : + *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Internal Error (rc = %d) at %s", rc, var->name); + return -1; + break; + + } + + /* No match. */ + return 0; +} + +/* validateUtf8Encoding */ + +#define UNICODE_ERROR_CHARACTERS_MISSING -1 +#define UNICODE_ERROR_INVALID_ENCODING -2 +#define UNICODE_ERROR_OVERLONG_CHARACTER -3 +#define UNICODE_ERROR_RESTRICTED_CHARACTER -4 +#define UNICODE_ERROR_DECODING_ERROR -5 + +/* NOTE: This is over-commented for ease of verification */ +static int detect_utf8_character(const unsigned char *p_read, unsigned int length) { + int unicode_len = 0; + unsigned int d = 0; + unsigned char c; + + if (p_read == NULL) return UNICODE_ERROR_DECODING_ERROR; + c = *p_read; + + /* If first byte begins with binary 0 it is single byte encoding */ + if ((c & 0x80) == 0) { + /* single byte unicode (7 bit ASCII equivilent) has no validation */ + return 1; + } + /* If first byte begins with binary 110 it is two byte encoding*/ + else if ((c & 0xE0) == 0xC0) { + /* check we have at least two bytes */ + if (length < 2) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; + /* check second byte starts with binary 10 */ + else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; + else { + unicode_len = 2; + /* compute character number */ + d = ((c & 0x1F) << 6) | (*(p_read + 1) & 0x3F); + } + } + /* If first byte begins with binary 1110 it is three byte encoding */ + else if ((c & 0xF0) == 0xE0) { + /* check we have at least three bytes */ + if (length < 3) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; + /* check second byte starts with binary 10 */ + else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; + /* check third byte starts with binary 10 */ + else if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; + else { + unicode_len = 3; + /* compute character number */ + d = ((c & 0x0F) << 12) | ((*(p_read + 1) & 0x3F) << 6) | (*(p_read + 2) & 0x3F); + } + } + /* If first byte begins with binary 11110 it is four byte encoding */ + else if ((c & 0xF8) == 0xF0) { + /* restrict characters to UTF-8 range (U+0000 - U+10FFFF)*/ + if (c >= 0xF5) { + return UNICODE_ERROR_RESTRICTED_CHARACTER; + } + /* check we have at least four bytes */ + if (length < 4) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; + /* check second byte starts with binary 10 */ + else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; + /* check third byte starts with binary 10 */ + else if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; + /* check forth byte starts with binary 10 */ + else if (((*(p_read + 3)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; + else { + unicode_len = 4; + /* compute character number */ + d = ((c & 0x07) << 18) | ((*(p_read + 1) & 0x3F) << 12) | ((*(p_read + 2) & 0x3F) < 6) | (*(p_read + 3) & 0x3F); + } + } + /* any other first byte is invalid (RFC 3629) */ + else { + return UNICODE_ERROR_INVALID_ENCODING; + } + + /* invalid UTF-8 character number range (RFC 3629) */ + if ((d >= 0xD800) && (d <= 0xDFFF)) { + return UNICODE_ERROR_RESTRICTED_CHARACTER; + } + + /* check for overlong */ + if ((unicode_len == 4) && (d < 0x010000)) { + /* four byte could be represented with less bytes */ + return UNICODE_ERROR_OVERLONG_CHARACTER; + } + else if ((unicode_len == 3) && (d < 0x0800)) { + /* three byte could be represented with less bytes */ + return UNICODE_ERROR_OVERLONG_CHARACTER; + } + else if ((unicode_len == 2) && (d < 0x80)) { + /* two byte could be represented with less bytes */ + return UNICODE_ERROR_OVERLONG_CHARACTER; + } + + return unicode_len; +} + +static int msre_op_validateUtf8Encoding_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, + char **error_msg) +{ + unsigned int i, bytes_left; + + bytes_left = var->value_len; + + for(i = 0; i < var->value_len;) { + int rc = detect_utf8_character((unsigned char *)&var->value[i], bytes_left); + + switch(rc) { + case UNICODE_ERROR_CHARACTERS_MISSING : + *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: " + "not enough bytes in character " + "at %s. [offset \"%d\"]", var->name, i); + return 1; + break; + case UNICODE_ERROR_INVALID_ENCODING : + *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: " + "invalid byte value in character " + "at %s. [offset \"%d\"]", var->name, i); + return 1; + break; + case UNICODE_ERROR_OVERLONG_CHARACTER : + *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: " + "overlong character detected " + "at %s. [offset \"%d\"]", var->name, i); + return 1; + break; + case UNICODE_ERROR_RESTRICTED_CHARACTER : + *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: " + "use of restricted character " + "at %s. [offset \"%d\"]", var->name, i); + return 1; + break; + case UNICODE_ERROR_DECODING_ERROR : + *error_msg = apr_psprintf(msr->mp, "Error validating UTF-8 decoding " + "at %s. [offset \"%d\"]", var->name, i); + return 1; + break; + } + + if (rc <= 0) { + *error_msg = apr_psprintf(msr->mp, "Internal error during UTF-8 validation " + "at %s.", var->name); + return 1; + } + + i += rc; + bytes_left -= rc; + } + + return 0; +} + +/* eq */ + +static int msre_op_eq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, + char **error_msg) +{ + msc_string str; + int left, right; + char *target = NULL; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + str.value = (char *)rule->op_param; + str.value_len = strlen(str.value); + + expand_macros(msr, &str, rule, msr->mp); + + target = apr_pstrmemdup(msr->mp, var->value, var->value_len); + if (target == NULL) return -1; + left = atoi(target); + right = atoi(str.value); + + if (left != right) { + /* No match. */ + return 0; + } + else { + *error_msg = apr_psprintf(msr->mp, "Operator EQ matched %d at %s.", right, var->name); + /* Match. */ + return 1; + } +} + +/* gt */ + +static int msre_op_gt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, + char **error_msg) +{ + msc_string str; + int left, right; + char *target = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + str.value = (char *)rule->op_param; + str.value_len = strlen(str.value); + + expand_macros(msr, &str, rule, msr->mp); + + target = apr_pstrmemdup(msr->mp, var->value, var->value_len); + if (target == NULL) return -1; + left = atoi(target); + right = atoi(str.value); + + if (left <= right) { + /* No match. */ + return 0; + } + else { + *error_msg = apr_psprintf(msr->mp, "Operator GT matched %d at %s.", right, var->name); + /* Match. */ + return 1; + } +} + +/* lt */ + +static int msre_op_lt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, + char **error_msg) +{ + msc_string str; + int left, right; + char *target = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + str.value = (char *)rule->op_param; + str.value_len = strlen(str.value); + + expand_macros(msr, &str, rule, msr->mp); + + target = apr_pstrmemdup(msr->mp, var->value, var->value_len); + if (target == NULL) return -1; + left = atoi(target); + right = atoi(str.value); + + if (left >= right) { + /* No match. */ + return 0; + } + else { + *error_msg = apr_psprintf(msr->mp, "Operator LT matched %d at %s.", right, var->name); + /* Match. */ + return 1; + } +} + +/* ge */ + +static int msre_op_ge_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, + char **error_msg) +{ + msc_string str; + int left, right; + char *target = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + str.value = (char *)rule->op_param; + str.value_len = strlen(str.value); + + expand_macros(msr, &str, rule, msr->mp); + + target = apr_pstrmemdup(msr->mp, var->value, var->value_len); + if (target == NULL) return -1; + left = atoi(target); + right = atoi(str.value); + + if (left < right) { + /* No match. */ + return 0; + } + else { + *error_msg = apr_psprintf(msr->mp, "Operator GE matched %d at %s.", right, var->name); + /* Match. */ + return 1; + } +} + +/* le */ + +static int msre_op_le_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, + char **error_msg) +{ + msc_string str; + int left, right; + char *target = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + str.value = (char *)rule->op_param; + str.value_len = strlen(str.value); + + expand_macros(msr, &str, rule, msr->mp); + + target = apr_pstrmemdup(msr->mp, var->value, var->value_len); + if (target == NULL) return -1; + left = atoi(target); + right = atoi(str.value); + + if (left > right) { + /* No match. */ + return 0; + } + else { + *error_msg = apr_psprintf(msr->mp, "Operator LE matched %d at %s.", right, var->name); + /* Match. */ + return 1; + } +} + +/* -------------------------------------------------------------------------- */ + +/** + * + */ +void msre_engine_register_default_operators(msre_engine *engine) { + /* unconditionalMatch */ + msre_engine_op_register(engine, + "unconditionalMatch", + NULL, + msre_op_unconditionalmatch_execute + ); + + /* noMatch */ + msre_engine_op_register(engine, + "noMatch", + NULL, + msre_op_nomatch_execute + ); + + /* rx */ + msre_engine_op_register(engine, + "rx", + msre_op_rx_param_init, + msre_op_rx_execute + ); + + /* pm */ + msre_engine_op_register(engine, + "pm", + msre_op_pm_param_init, + msre_op_pm_execute + ); + + /* pmFromFile */ + msre_engine_op_register(engine, + "pmFromFile", + msre_op_pmFromFile_param_init, + msre_op_pm_execute + ); + + /* within */ + msre_engine_op_register(engine, + "within", + NULL, /* ENH init function to flag var substitution */ + msre_op_within_execute + ); + + /* contains */ + msre_engine_op_register(engine, + "contains", + NULL, /* ENH init function to flag var substitution */ + msre_op_contains_execute + ); + + /* containsWord */ + msre_engine_op_register(engine, + "containsWord", + NULL, /* ENH init function to flag var substitution */ + msre_op_containsWord_execute + ); + + /* is */ + msre_engine_op_register(engine, + "streq", + NULL, /* ENH init function to flag var substitution */ + msre_op_streq_execute + ); + + /* beginsWith */ + msre_engine_op_register(engine, + "beginsWith", + NULL, /* ENH init function to flag var substitution */ + msre_op_beginsWith_execute + ); + + /* endsWith */ + msre_engine_op_register(engine, + "endsWith", + NULL, /* ENH init function to flag var substitution */ + msre_op_endsWith_execute + ); + + /* m */ + msre_engine_op_register(engine, + "m", + msre_op_m_param_init, + msre_op_m_execute + ); + + /* validateDTD */ + msre_engine_op_register(engine, + "validateDTD", + msre_op_validateDTD_init, + msre_op_validateDTD_execute + ); + + /* validateSchema */ + msre_engine_op_register(engine, + "validateSchema", + msre_op_validateSchema_init, + msre_op_validateSchema_execute + ); + + /* verifyCC */ + msre_engine_op_register(engine, + "verifyCC", + msre_op_verifyCC_init, + msre_op_verifyCC_execute + ); + + /* geoLookup */ + msre_engine_op_register(engine, + "geoLookup", + NULL, + msre_op_geoLookup_execute + ); + + /* rbl */ + msre_engine_op_register(engine, + "rbl", + NULL, /* ENH init function to validate DNS server */ + msre_op_rbl_execute + ); + + /* inspectFile */ + msre_engine_op_register(engine, + "inspectFile", + msre_op_inspectFile_init, + msre_op_inspectFile_execute + ); + + /* validateByteRange */ + msre_engine_op_register(engine, + "validateByteRange", + msre_op_validateByteRange_init, + msre_op_validateByteRange_execute + ); + + /* validateUrlEncoding */ + msre_engine_op_register(engine, + "validateUrlEncoding", + NULL, + msre_op_validateUrlEncoding_execute + ); + + /* validateUtf8Encoding */ + msre_engine_op_register(engine, + "validateUtf8Encoding", + NULL, + msre_op_validateUtf8Encoding_execute + ); + + /* eq */ + msre_engine_op_register(engine, + "eq", + NULL, + msre_op_eq_execute + ); + + /* gt */ + msre_engine_op_register(engine, + "gt", + NULL, + msre_op_gt_execute + ); + + /* lt */ + msre_engine_op_register(engine, + "lt", + NULL, + msre_op_lt_execute + ); + + /* le */ + msre_engine_op_register(engine, + "le", + NULL, + msre_op_le_execute + ); + + /* ge */ + msre_engine_op_register(engine, + "ge", + NULL, + msre_op_ge_execute + ); +} diff --git a/2.5.13/2.5.x/apache2/re_tfns.c b/2.5.13/2.5.x/apache2/re_tfns.c new file mode 100644 index 00000000..711bd696 --- /dev/null +++ b/2.5.13/2.5.x/apache2/re_tfns.c @@ -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 + +#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 + ); +} diff --git a/2.5.13/2.5.x/apache2/re_variables.c b/2.5.13/2.5.x/apache2/re_variables.c new file mode 100644 index 00000000..c3e58a07 --- /dev/null +++ b/2.5.13/2.5.x/apache2/re_variables.c @@ -0,0 +1,3188 @@ +/* + * 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 "http_core.h" + +#include "modsecurity.h" +#include "apache2.h" +#include "re.h" +#include "msc_util.h" + +#include "libxml/xpathInternals.h" + +/** + * Generates a variable from a string and a length. + */ +static int var_simple_generate_ex(msre_var *var, apr_table_t *vartab, apr_pool_t *mptmp, + const char *value, int value_len) +{ + msre_var *rvar = NULL; + + if (value == NULL) return 0; + + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = value; + rvar->value_len = value_len; + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/** + * Generates a variable from a NULL-terminated string. + */ +static int var_simple_generate(msre_var *var, apr_table_t *vartab, apr_pool_t *mptmp, + const char *value) +{ + if (value == NULL) return 0; + return var_simple_generate_ex(var, vartab, mptmp, value, strlen(value)); +} + +/** + * Validate that a target parameter is valid. We only need to take + * care of the case when the parameter is a regular expression. + */ +static char *var_generic_list_validate(msre_ruleset *ruleset, msre_var *var) { + /* It's OK if there's no parameter. */ + if (var->param == NULL) return NULL; + + /* Is it a regular expression? */ + if ((strlen(var->param) > 2)&&(var->param[0] == '/') + &&(var->param[strlen(var->param) - 1] == '/')) + { /* Regex. */ + msc_regex_t *regex = NULL; + const char *errptr = NULL; + const char *pattern = NULL; + int erroffset; + + pattern = apr_pstrmemdup(ruleset->mp, var->param + 1, strlen(var->param + 1) - 1); + if (pattern == NULL) return FATAL_ERROR; + + regex = msc_pregcomp(ruleset->mp, pattern, PCRE_DOTALL | PCRE_CASELESS | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset); + if (regex == NULL) { + return apr_psprintf(ruleset->mp, "Error compiling pattern (offset %d): %s", + erroffset, errptr); + } + + /* Store the compiled regex for later. */ + var->param_data = regex; + } + + /* Simple string */ + return NULL; +} + +/* Custom parameter validation functions */ + +/* ARGS */ + +static int var_args_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + /* Loop through the arguments. */ + arr = apr_table_elts(msr->arguments); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_arg *arg = (msc_arg *)te[i].val; + int match = 0; + + /* Figure out if we want to include this argument. */ + if (var->param == NULL) match = 1; /* Unconditional inclusion. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + /* Run the regex against the argument name. */ + if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, + arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(arg->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = arg->value; + rvar->value_len = arg->value_len; + rvar->name = apr_psprintf(mptmp, "ARGS:%s", log_escape_nq_ex(mptmp, arg->name, arg->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* ARGS_COMBINED_SIZE */ + +static int var_args_combined_size_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + unsigned int combined_size = 0; + int i; + msre_var *rvar = NULL; + + arr = apr_table_elts(msr->arguments); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_arg *arg = (msc_arg *)te[i].val; + combined_size += arg->name_len; + combined_size += arg->value_len; + } + + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_psprintf(mptmp, "%u", combined_size); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* ARGS_NAMES */ + +static int var_args_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + arr = apr_table_elts(msr->arguments); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_arg *arg = (msc_arg *)te[i].val; + int match = 0; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; /* Unconditional inclusion. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, + arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(arg->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = arg->name; + rvar->value_len = arg->name_len; + rvar->name = apr_psprintf(mptmp, "ARGS_NAMES:%s", log_escape_nq_ex(mptmp, arg->name, arg->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* ARGS_GET */ + +static int var_args_get_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + /* Loop through the arguments. */ + arr = apr_table_elts(msr->arguments); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_arg *arg = (msc_arg *)te[i].val; + int match = 0; + + /* Only QUERY_STRING arguments */ + if (strcmp("QUERY_STRING", arg->origin) != 0) continue; + + /* Figure out if we want to include this argument. */ + if (var->param == NULL) match = 1; /* Unconditional inclusion. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + /* Run the regex against the argument name. */ + if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, + arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(arg->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = arg->value; + rvar->value_len = arg->value_len; + rvar->name = apr_psprintf(mptmp, "ARGS_GET:%s", log_escape_nq_ex(mptmp, arg->name, arg->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* ARGS_GET_NAMES */ + +static int var_args_get_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + arr = apr_table_elts(msr->arguments); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_arg *arg = (msc_arg *)te[i].val; + int match = 0; + + /* Only QUERY_STRING arguments */ + if (strcmp("QUERY_STRING", arg->origin) != 0) continue; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; /* Unconditional inclusion. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, + arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(arg->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = arg->name; + rvar->value_len = arg->name_len; + rvar->name = apr_psprintf(mptmp, "ARGS_GET_NAMES:%s", log_escape_nq_ex(mptmp, arg->name, arg->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* ARGS_POST */ + +static int var_args_post_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + /* Loop through the arguments. */ + arr = apr_table_elts(msr->arguments); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_arg *arg = (msc_arg *)te[i].val; + int match = 0; + + /* Only BODY arguments */ + if (strcmp("BODY", arg->origin) != 0) continue; + + /* Figure out if we want to include this argument. */ + if (var->param == NULL) match = 1; /* Unconditional inclusion. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + /* Run the regex against the argument name. */ + if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, + arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(arg->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = arg->value; + rvar->value_len = arg->value_len; + rvar->name = apr_psprintf(mptmp, "ARGS_POST:%s", log_escape_nq_ex(mptmp, arg->name, arg->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* ARGS_POST_NAMES */ + +static int var_args_post_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + arr = apr_table_elts(msr->arguments); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_arg *arg = (msc_arg *)te[i].val; + int match = 0; + + /* Only BODY arguments */ + if (strcmp("BODY", arg->origin) != 0) continue; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; /* Unconditional inclusion. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, + arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(arg->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = arg->name; + rvar->value_len = arg->name_len; + rvar->name = apr_psprintf(mptmp, "ARGS_POST_NAMES:%s", log_escape_nq_ex(mptmp, arg->name, arg->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* RULE */ + +static int var_rule_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_actionset *actionset = NULL; + + if (rule == NULL) return 0; + actionset = rule->actionset; + if (rule->chain_starter != NULL) actionset = rule->chain_starter->actionset; + + if ((strcasecmp(var->param, "id") == 0)&&(actionset->id != NULL)) { + return var_simple_generate(var, vartab, mptmp, actionset->id); + } else + if ((strcasecmp(var->param, "rev") == 0)&&(actionset->rev != NULL)) { + return var_simple_generate(var, vartab, mptmp, actionset->rev); + } else + if ((strcasecmp(var->param, "severity") == 0)&&(actionset->severity != -1)) { + char *value = apr_psprintf(mptmp, "%d", actionset->severity); + return var_simple_generate(var, vartab, mptmp, value); + } else + if ((strcasecmp(var->param, "msg") == 0)&&(actionset->msg != NULL)) { + return var_simple_generate(var, vartab, mptmp, actionset->msg); + } else + if ((strcasecmp(var->param, "logdata") == 0)&&(actionset->logdata != NULL)) { + return var_simple_generate(var, vartab, mptmp, actionset->logdata); + } + + return 0; +} + +/* ENV */ + +static char *var_env_validate(msre_ruleset *ruleset, msre_var *var) { + if (var->param == NULL) { + return apr_psprintf(ruleset->mp, "Parameter required for ENV."); + } + if ((strlen(var->param) > 2)&&(var->param[0] == '/') + &&(var->param[strlen(var->param) - 1] == '/')) + { + return apr_psprintf(ruleset->mp, "Regular expressions not supported in ENV."); + } + return NULL; +} + +static int var_env_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + char *value = get_env_var(msr->r, (char *)var->param); + if (value != NULL) { + return var_simple_generate(var, vartab, mptmp, value); + } + return 0; +} + +/* REQUEST_URI_RAW */ + +static int var_request_uri_raw_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, msr->r->unparsed_uri); +} + +/* REQUEST_URI */ + +static int var_request_uri_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) /* dynamic */ +{ + char *value = NULL; + + if (msr->r->parsed_uri.query == NULL) value = msr->r->parsed_uri.path; + else value = apr_pstrcat(mptmp, msr->r->parsed_uri.path, "?", msr->r->parsed_uri.query, NULL); + + return var_simple_generate(var, vartab, mptmp, value); +} + +/* REQBODY_PROCESSOR */ + +static int var_reqbody_processor_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + if (msr->msc_reqbody_processor == NULL) { + rvar->value = apr_pstrdup(mptmp, ""); + rvar->value_len = 0; + } else { + rvar->value = apr_pstrdup(mptmp, msr->msc_reqbody_processor); + rvar->value_len = strlen(rvar->value); + } + + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* REQBODY_ERROR */ + +static int var_reqbody_processor_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = apr_psprintf(mptmp, "%d", msr->msc_reqbody_error); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* REQBODY_ERROR_MSG */ + +static int var_reqbody_processor_error_msg_generate(modsec_rec *msr, msre_var *var, + msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + if (msr->msc_reqbody_error_msg == NULL) { + rvar->value = apr_pstrdup(mptmp, ""); + rvar->value_len = 0; + } else { + rvar->value = apr_psprintf(mptmp, "%s", msr->msc_reqbody_error_msg); + rvar->value_len = strlen(rvar->value); + } + + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* XML */ + +static char *var_xml_validate(msre_ruleset *ruleset, msre_var *var) { + /* It's OK if there's no parameter. */ + if (var->param == NULL) return NULL; + + /* ENH validate XPath expression in advance. */ + + return NULL; +} + +static int var_xml_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + xmlXPathContextPtr xpathCtx; + xmlXPathObjectPtr xpathObj; + xmlNodeSetPtr nodes; + const xmlChar* xpathExpr = NULL; + int i, count; + + /* Is there an XML document tree at all? */ + if ((msr->xml == NULL)||(msr->xml->doc == NULL)) { + /* Sorry, we've got nothing to give! */ + return 0; + } + + if (var->param == NULL) { + /* Invocation without an XPath expression makes sense + * with functions that manipulate the document tree. + */ + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = apr_pstrdup(mptmp, "[XML document tree]"); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; + } + + /* Process the XPath expression. */ + + count = 0; + xpathExpr = (const xmlChar*)var->param; + + xpathCtx = xmlXPathNewContext(msr->xml->doc); + if (xpathCtx == NULL) { + msr_log(msr, 1, "XML: Unable to create new XPath context."); + return -1; + } + + /* Look through the actionset of the associated rule + * for the namespace information. Register them if any are found. + */ + tarr = apr_table_elts(rule->actionset->actions); + telts = (const apr_table_entry_t*)tarr->elts; + for (i = 0; i < tarr->nelts; i++) { + msre_action *action = (msre_action *)telts[i].val; + + if (strcasecmp(action->metadata->name, "xmlns") == 0) { + char *prefix, *href; + + if (parse_name_eq_value(mptmp, action->param, &prefix, &href) < 0) return -1; + if ((prefix == NULL)||(href == NULL)) return -1; + + if(xmlXPathRegisterNs(xpathCtx, (const xmlChar*)prefix, (const xmlChar*)href) != 0) { + msr_log(msr, 1, "Failed to register XML namespace href \"%s\" prefix \"%s\".", + log_escape(mptmp, prefix), log_escape(mptmp, href)); + return -1; + } + + msr_log(msr, 4, "Registered XML namespace href \"%s\" prefix \"%s\".", + log_escape(mptmp, prefix), log_escape(mptmp, href)); + } + } + + /* Initialise XPath expression. */ + xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx); + if (xpathObj == NULL) { + msr_log(msr, 1, "XML: Unable to evaluate xpath expression."); + xmlXPathFreeContext(xpathCtx); + return -1; + } + + /* Evaluate XPath expression. */ + nodes = xpathObj->nodesetval; + if (nodes == NULL) { + xmlXPathFreeObject(xpathObj); + xmlXPathFreeContext(xpathCtx); + return 0; + } + + /* Create one variable for each node in the result. */ + for(i = 0; i < nodes->nodeNr; i++) { + msre_var *rvar = NULL; + char *content = NULL; + + content = (char *)xmlNodeGetContent(nodes->nodeTab[i]); + if (content != NULL) { + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_pstrdup(mptmp, content); + xmlFree(content); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + xmlXPathFreeObject(xpathObj); + xmlXPathFreeContext(xpathCtx); + + return count; +} + +/* WEBSERVER_ERROR_LOG */ + +static int var_webserver_error_log_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = NULL; + int i, count = 0; + + for(i = 0; i < msr->error_messages->nelts; i++) { + error_message *em = (((error_message**)msr->error_messages->elts)[i]); + char *fem = NULL; + + fem = format_error_log_message(mptmp, em); + if (fem != NULL) { + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_pstrdup(mptmp, fem); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* REMOTE_ADDR */ + +static int var_remote_addr_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, msr->remote_addr); +} + +/* REMOTE_HOST */ + +static int var_remote_host_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const char *value1 = ap_get_remote_host(msr->r->connection, msr->r->per_dir_config, + REMOTE_NAME, NULL); + return var_simple_generate(var, vartab, mptmp, value1); +} + +/* REMOTE_PORT */ + +static int var_remote_port_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + char *value = apr_psprintf(mptmp, "%u", msr->remote_port); + return var_simple_generate(var, vartab, mptmp, value); +} + +/* REMOTE_USER */ + +static int var_remote_user_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, msr->remote_user); +} + +/* TX */ + +static int var_tx_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + arr = apr_table_elts(msr->tx_vars); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_string *str = (msc_string *)te[i].val; + int match; + + /* Figure out if we want to include this variable. */ + match = 0; + if (var->param == NULL) match = 1; /* Unconditional inclusion. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, + str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(str->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = str->value; + rvar->value_len = str->value_len; + rvar->name = apr_psprintf(mptmp, "TX:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* GEO */ + +static int var_geo_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + arr = apr_table_elts(msr->geo_vars); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_string *str = (msc_string *)te[i].val; + int match; + + /* Figure out if we want to include this variable. */ + match = 0; + if (var->param == NULL) match = 1; /* Unconditional inclusion. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, + str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(str->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = str->value; + rvar->value_len = str->value_len; + rvar->name = apr_psprintf(mptmp, "GEO:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* HIGHEST_SEVERITY */ + +static int var_highest_severity_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, + apr_psprintf(mptmp, "%d", msr->highest_severity)); +} + +/* IP */ + +static int var_ip_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + apr_table_t *target_col = NULL; + + target_col = (apr_table_t *)apr_table_get(msr->collections, "ip"); + if (target_col == NULL) return 0; + + arr = apr_table_elts(target_col); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_string *str = (msc_string *)te[i].val; + int match; + + /* Figure out if we want to include this variable. */ + match = 0; + if (var->param == NULL) match = 1; /* Unconditional inclusion. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, + str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(str->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = str->value; + rvar->value_len = str->value_len; + rvar->name = apr_psprintf(mptmp, "IP:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* MATCHED_VAR */ + +static int var_matched_var_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate_ex(var, vartab, mptmp, + apr_pmemdup(mptmp, + msr->matched_var->value, + msr->matched_var->value_len), + msr->matched_var->value_len); +} + +/* MATCHED_VAR_NAME */ + +static int var_matched_var_name_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate_ex(var, vartab, mptmp, + apr_pmemdup(mptmp, + msr->matched_var->name, + msr->matched_var->name_len), + msr->matched_var->name_len); +} + +/* SESSION */ + +static int var_session_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + apr_table_t *target_col = NULL; + + target_col = (apr_table_t *)apr_table_get(msr->collections, "session"); + if (target_col == NULL) return 0; + + arr = apr_table_elts(target_col); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_string *str = (msc_string *)te[i].val; + int match; + + /* Figure out if we want to include this variable. */ + match = 0; + if (var->param == NULL) match = 1; /* Unconditional inclusion. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, + str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(str->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = str->value; + rvar->value_len = str->value_len; + rvar->name = apr_psprintf(mptmp, "SESSION:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* USER */ + +static int var_user_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + apr_table_t *target_col = NULL; + + target_col = (apr_table_t *)apr_table_get(msr->collections, "user"); + if (target_col == NULL) return 0; + + arr = apr_table_elts(target_col); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_string *str = (msc_string *)te[i].val; + int match; + + /* Figure out if we want to include this variable. */ + match = 0; + if (var->param == NULL) match = 1; /* Unconditional match. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, + str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(str->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = str->value; + rvar->value_len = str->value_len; + rvar->name = apr_psprintf(mptmp, "USER:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* GLOBAL */ + +static int var_global_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + apr_table_t *target_col = NULL; + + target_col = (apr_table_t *)apr_table_get(msr->collections, "global"); + if (target_col == NULL) return 0; + + arr = apr_table_elts(target_col); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_string *str = (msc_string *)te[i].val; + int match; + + /* Figure out if we want to include this variable. */ + match = 0; + if (var->param == NULL) match = 1; /* Unconditional match. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, + str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(str->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = str->value; + rvar->value_len = str->value_len; + rvar->name = apr_psprintf(mptmp, "GLOBAL:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* RESOURCE */ + +static int var_resource_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + apr_table_t *target_col = NULL; + + target_col = (apr_table_t *)apr_table_get(msr->collections, "resource"); + if (target_col == NULL) return 0; + + arr = apr_table_elts(target_col); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_string *str = (msc_string *)te[i].val; + int match; + + /* Figure out if we want to include this variable. */ + match = 0; + if (var->param == NULL) match = 1; /* Unconditional match. */ + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, + str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(str->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = str->value; + rvar->value_len = str->value_len; + rvar->name = apr_psprintf(mptmp, "RESOURCE:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* FILES_TMPNAMES */ + +static int var_files_tmpnames_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + multipart_part **parts = NULL; + int i, count = 0; + + if (msr->mpd == NULL) return 0; + + parts = (multipart_part **)msr->mpd->parts->elts; + for(i = 0; i < msr->mpd->parts->nelts; i++) { + if ((parts[i]->type == MULTIPART_FILE)&&(parts[i]->tmp_file_name != NULL)) { + int match = 0; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, + strlen(parts[i]->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(parts[i]->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = parts[i]->tmp_file_name; + rvar->value_len = strlen(rvar->value); + rvar->name = apr_psprintf(mptmp, "FILES_TMPNAMES:%s", + log_escape_nq(mptmp, parts[i]->name)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + } + + return count; +} + +/* FILES */ + +static int var_files_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + multipart_part **parts = NULL; + int i, count = 0; + + if (msr->mpd == NULL) return 0; + + parts = (multipart_part **)msr->mpd->parts->elts; + for(i = 0; i < msr->mpd->parts->nelts; i++) { + if (parts[i]->type == MULTIPART_FILE) { + int match = 0; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, + strlen(parts[i]->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(parts[i]->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = parts[i]->filename; + rvar->value_len = strlen(rvar->value); + rvar->name = apr_psprintf(mptmp, "FILES:%s", + log_escape_nq(mptmp, parts[i]->name)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + } + + return count; +} + +/* FILES_SIZES */ + +static int var_files_sizes_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + multipart_part **parts = NULL; + int i, count = 0; + + if (msr->mpd == NULL) return 0; + + parts = (multipart_part **)msr->mpd->parts->elts; + for(i = 0; i < msr->mpd->parts->nelts; i++) { + if (parts[i]->type == MULTIPART_FILE) { + int match = 0; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, + strlen(parts[i]->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(parts[i]->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = apr_psprintf(mptmp, "%u", parts[i]->tmp_file_size); + rvar->value_len = strlen(rvar->value); + rvar->name = apr_psprintf(mptmp, "FILES_SIZES:%s", + log_escape_nq(mptmp, parts[i]->name)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + } + + return count; +} + +/* FILES_NAMES */ + +static int var_files_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + multipart_part **parts = NULL; + int i, count = 0; + + if (msr->mpd == NULL) return 0; + + parts = (multipart_part **)msr->mpd->parts->elts; + for(i = 0; i < msr->mpd->parts->nelts; i++) { + if (parts[i]->type == MULTIPART_FILE) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = parts[i]->name; + rvar->value_len = strlen(rvar->value); + rvar->name = apr_psprintf(mptmp, "FILES_NAMES:%s", + log_escape_nq_ex(mptmp, parts[i]->name, rvar->value_len)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* FILES_COMBINED_SIZE */ + +static int var_files_combined_size_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + multipart_part **parts = NULL; + msre_var *rvar = NULL; + unsigned int combined_size = 0; + int i; + + if (msr->mpd != NULL) { + parts = (multipart_part **)msr->mpd->parts->elts; + for(i = 0; i < msr->mpd->parts->nelts; i++) { + if (parts[i]->type == MULTIPART_FILE) { + combined_size += parts[i]->tmp_file_size; + } + } + } + + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_psprintf(mptmp, "%u", combined_size); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* MODSEC_BUILD */ + +static int var_modsec_build_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, modsec_build(mptmp)); +} + +/* MULTIPART_BOUNDARY_QUOTED */ + +static int var_multipart_boundary_quoted_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_boundary_quoted != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_BOUNDARY_WHITESPACE */ + +static int var_multipart_boundary_whitespace_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_boundary_whitespace != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_DATA_AFTER */ + +static int var_multipart_data_after_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_data_after != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_DATA_BEFORE */ + +static int var_multipart_data_before_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_data_before != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_HEADER_FOLDING */ + +static int var_multipart_header_folding_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_header_folding != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_CRLF_LINE */ + +static int var_multipart_crlf_line_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_crlf_line != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_CRLF_LF_LINES */ + +static int var_multipart_crlf_lf_lines_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_lf_line != 0)&&(msr->mpd->flag_crlf_line != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_LF_LINE */ + +static int var_multipart_lf_line_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_lf_line != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_MISSING_SEMICOLON */ + +static int var_multipart_missing_semicolon_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_missing_semicolon != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_INVALID_QUOTING */ + +static int var_multipart_invalid_quoting_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_invalid_quoting != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_INVALID_HEADER_FOLDING */ + +static int var_multipart_invalid_header_folding_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_invalid_header_folding != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_FILE_LIMIT_EXCEEDED */ + +static int var_multipart_file_limit_exceeded_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_file_limit_exceeded != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_STRICT_ERROR */ + +static int var_multipart_strict_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if (msr->mpd != NULL) { + /* Respond positive if at least one of the multipart flags is raised. */ + if ( (msr->mpd->flag_error) + ||(msr->mpd->flag_boundary_quoted != 0) + ||(msr->mpd->flag_boundary_whitespace != 0) + ||(msr->mpd->flag_data_before != 0) + ||(msr->mpd->flag_data_after != 0) + ||(msr->mpd->flag_header_folding != 0) + ||(msr->mpd->flag_lf_line != 0) + ||(msr->mpd->flag_missing_semicolon != 0) + ||(msr->mpd->flag_invalid_quoting != 0) + ||(msr->mpd->flag_invalid_header_folding != 0) + ||(msr->mpd->flag_file_limit_exceeded != 0) + ) { + return var_simple_generate(var, vartab, mptmp, "1"); + } + } + + return var_simple_generate(var, vartab, mptmp, "0"); +} + +/* MULTIPART_UNMATCHED_BOUNDARY */ + +static int var_multipart_unmatched_boundary_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_unmatched_boundary != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* TIME */ + +static int var_time_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = NULL; + struct tm *tm; + time_t tc; + + tc = time(NULL); + tm = localtime(&tc); + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_psprintf(mptmp, "%02d%02d%02d%02d%02d%02d%02d", + (tm->tm_year / 100) + 19, (tm->tm_year % 100), + tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* TIME_YEAR */ + +static int var_time_year_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = NULL; + struct tm *tm; + time_t tc; + + tc = time(NULL); + tm = localtime(&tc); + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_psprintf(mptmp, "%02d%02d", + (tm->tm_year / 100) + 19, + tm->tm_year % 100); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* TIME_WDAY */ + +static int var_time_wday_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = NULL; + struct tm *tm; + time_t tc; + + tc = time(NULL); + tm = localtime(&tc); + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_psprintf(mptmp, "%d", tm->tm_wday); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* TIME_SEC */ + +static int var_time_sec_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = NULL; + struct tm *tm; + time_t tc; + + tc = time(NULL); + tm = localtime(&tc); + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_sec); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* TIME_MIN */ + +static int var_time_min_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = NULL; + struct tm *tm; + time_t tc; + + tc = time(NULL); + tm = localtime(&tc); + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_min); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* TIME_HOUR */ +static int var_time_hour_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = NULL; + struct tm *tm; + time_t tc; + + tc = time(NULL); + tm = localtime(&tc); + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_hour); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* TIME_MON */ + +static int var_time_mon_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = NULL; + struct tm *tm; + time_t tc; + + tc = time(NULL); + tm = localtime(&tc); + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_mon + 1); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* TIME_DAY */ + +static int var_time_day_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = NULL; + struct tm *tm; + time_t tc; + + tc = time(NULL); + tm = localtime(&tc); + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_mday); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* TIME_EPOCH */ + +static int var_time_epoch_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + msre_var *rvar = NULL; + struct tm *tm; + time_t tc; + + tc = time(NULL); + tm = localtime(&tc); + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = apr_psprintf(mptmp, "%ld", (long)tc); + rvar->value_len = strlen(rvar->value); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/* QUERY_STRING */ + +static int var_query_string_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, msr->query_string); +} + +/* REQUEST_BASENAME */ + +static int var_request_basename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + char *value = file_basename(mptmp, msr->r->parsed_uri.path); + return var_simple_generate(var, vartab, mptmp, value); +} + +/* REQUEST_BODY */ + +static int var_request_body_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if (msr->msc_reqbody_buffer != NULL) { + return var_simple_generate_ex(var, vartab, mptmp, + msr->msc_reqbody_buffer, msr->msc_reqbody_length); + } + return 0; +} + +/* REQUEST_COOKIES */ + +static int var_request_cookies_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + arr = apr_table_elts(msr->request_cookies); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + int match = 0; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, + strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(te[i].key, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = te[i].val; + rvar->value_len = strlen(rvar->value); + rvar->name = apr_psprintf(mptmp, "REQUEST_COOKIES:%s", + log_escape_nq(mptmp, te[i].key)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* REQUEST_COOKIES_NAMES */ + +static int var_request_cookies_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + arr = apr_table_elts(msr->request_cookies); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + int match = 0; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, + strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(te[i].key, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = te[i].key; + rvar->value_len = strlen(rvar->value); + rvar->name = apr_psprintf(mptmp, "REQUEST_COOKIES_NAMES:%s", + log_escape_nq(mptmp, te[i].key)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* REQUEST_HEADERS */ + +static int var_request_headers_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + arr = apr_table_elts(msr->request_headers); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + int match = 0; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, + strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(te[i].key, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = te[i].val; + rvar->value_len = strlen(rvar->value); + rvar->name = apr_psprintf(mptmp, "REQUEST_HEADERS:%s", + log_escape_nq(mptmp, te[i].key)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* REQUEST_HEADERS_NAMES */ + +static int var_request_headers_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + arr = apr_table_elts(msr->request_headers); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + int match = 0; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, + strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(te[i].key, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = te[i].key; + rvar->value_len = strlen(rvar->value); + rvar->name = apr_psprintf(mptmp, "REQUEST_HEADERS_NAMES:%s", + log_escape_nq(mptmp, te[i].key)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* REQUEST_FILENAME */ + +static int var_request_filename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, msr->r->parsed_uri.path); +} + +/* REQUEST_LINE */ + +static int var_request_line_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, msr->request_line); +} + +/* REQUEST_METHOD */ + +static int var_request_method_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, msr->request_method); +} + +/* REQUEST_PROTOCOL */ + +static int var_request_protocol_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, msr->request_protocol); +} + +/* SERVER_ADDR */ + +static int var_server_addr_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, msr->local_addr); +} + +/* SERVER_NAME */ + +static int var_server_name_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, msr->hostname); +} + +/* SERVER_PORT */ + +static int var_server_port_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + char *value = apr_psprintf(mptmp, "%u", msr->local_port); + return var_simple_generate(var, vartab, mptmp, value); +} + +/* SCRIPT_BASENAME */ + +static int var_script_basename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + char *value = file_basename(mptmp, msr->r->filename); + return var_simple_generate(var, vartab, mptmp, value); +} + +/* SCRIPT_FILENAME */ + +static int var_script_filename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + char *value = msr->r->filename; + return var_simple_generate(var, vartab, mptmp, value); +} + +/* SCRIPT_GID */ + +static int var_script_gid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + char *value = apr_psprintf(mptmp, "%ld", (long)msr->r->finfo.group); + return var_simple_generate(var, vartab, mptmp, value); +} + +/* SCRIPT_GROUPNAME */ + +static int var_script_groupname_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + char *value = NULL; + if (apr_gid_name_get(&value, msr->r->finfo.group, mptmp) == APR_SUCCESS) { + return var_simple_generate(var, vartab, mptmp, value); + } + return 0; +} + +/* SCRIPT_MODE */ + +static int var_script_mode_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + char *value = apr_psprintf(mptmp, "%04x", msr->r->finfo.protection); + return var_simple_generate(var, vartab, mptmp, value); +} + +/* SCRIPT_UID */ + +static int var_script_uid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + char *value = apr_psprintf(mptmp, "%ld", (long)msr->r->finfo.user); + return var_simple_generate(var, vartab, mptmp, value); +} + +/* SCRIPT_USERNAME */ + +static int var_script_username_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + char *value = NULL; + if (apr_uid_name_get(&value, msr->r->finfo.user, mptmp) == APR_SUCCESS) { + return var_simple_generate(var, vartab, mptmp, value); + } + return 0; +} + +/* AUTH_TYPE */ + +static int var_auth_type_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + char *value = msr->r->ap_auth_type; + return var_simple_generate(var, vartab, mptmp, value); +} + +/* PATH_INFO */ + +static int var_path_info_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const char *value = msr->r->path_info; + return var_simple_generate(var, vartab, mptmp, value); +} + +/* RESPONSE_BODY */ + +static int var_response_body_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if (msr->resbody_data != NULL) { + return var_simple_generate_ex(var, vartab, mptmp, + msr->resbody_data, msr->resbody_length); + } + + return 0; +} + +/* RESPONSE_HEADERS */ + +static int var_response_headers_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + if (msr->response_headers == NULL) return 0; + + arr = apr_table_elts(msr->response_headers); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + int match = 0; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, + strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(te[i].key, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = te[i].val; + rvar->value_len = strlen(rvar->value); + rvar->name = apr_psprintf(mptmp, "RESPONSE_HEADERS:%s", + log_escape_nq(mptmp, te[i].key)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* RESPONSE_HEADERS_NAMES */ + +static int var_response_headers_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const apr_array_header_t *arr = NULL; + const apr_table_entry_t *te = NULL; + int i, count = 0; + + arr = apr_table_elts(msr->response_headers); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + int match = 0; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, + strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(te[i].key, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = te[i].key; + rvar->value_len = strlen(rvar->value); + rvar->name = apr_psprintf(mptmp, "RESPONSE_HEADERS_NAMES:%s", + log_escape_nq(mptmp, te[i].key)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + + return count; +} + +/* STATUS_LINE */ + +static int var_status_line_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const char *value = msr->status_line; + return var_simple_generate(var, vartab, mptmp, value); +} + +/* RESPONSE_PROTOCOL */ + +static int var_response_protocol_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const char *value = msr->response_protocol; + return var_simple_generate(var, vartab, mptmp, value); +} + +/* RESPONSE_STATUS */ + +static int var_response_status_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const char *value = apr_psprintf(mptmp, "%u", msr->response_status); + return var_simple_generate(var, vartab, mptmp, value); +} + +/* RESPONSE_CONTENT_TYPE */ + +static int var_response_content_type(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, msr->r->content_type); +} + +/* RESPONSE_CONTENT_LENGTH */ + +static int var_response_content_length(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const char *value = apr_psprintf(mptmp, "%" APR_OFF_T_FMT, msr->r->clength); + return var_simple_generate(var, vartab, mptmp, value); +} + +/* USERID */ + +static int var_userid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const char *value = msr->userid; + return var_simple_generate(var, vartab, mptmp, value); +} + +/* SESSIONID */ + +static int var_sessionid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const char *value = msr->sessionid; + return var_simple_generate(var, vartab, mptmp, value); +} + +/* WEBAPPID */ + +static int var_webappid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const char *value = msr->txcfg->webappid; + return var_simple_generate(var, vartab, mptmp, value); +} + +/* ---------------------------------------------- */ + +/** + * + */ +void msre_engine_variable_register(msre_engine *engine, const char *name, + unsigned int type, unsigned int argc_min, unsigned int argc_max, + fn_var_validate_t validate, fn_var_generate_t generate, + unsigned int is_cacheable, unsigned int availability) +{ + msre_var_metadata *metadata = (msre_var_metadata *)apr_pcalloc(engine->mp, + sizeof(msre_var_metadata)); + if (metadata == NULL) return; + + metadata->name = name; + metadata->type = type; + metadata->argc_min = argc_min; + metadata->argc_max = argc_max; + metadata->validate = validate; + metadata->generate = generate; + metadata->is_cacheable = is_cacheable; + metadata->availability = availability; + + apr_table_setn(engine->variables, name, (void *)metadata); +} + +/** + * + */ +void msre_engine_register_default_variables(msre_engine *engine) { + + /* ARGS */ + msre_engine_variable_register(engine, + "ARGS", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_args_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* ARGS_COMBINED_SIZE */ + msre_engine_variable_register(engine, + "ARGS_COMBINED_SIZE", + VAR_LIST, + 0, 0, + NULL, + var_args_combined_size_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* ARGS_GET */ + msre_engine_variable_register(engine, + "ARGS_GET", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_args_get_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* ARGS_GET_NAMES */ + msre_engine_variable_register(engine, + "ARGS_GET_NAMES", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_args_get_names_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* ARGS_NAMES */ + msre_engine_variable_register(engine, + "ARGS_NAMES", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_args_names_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* ARGS_POST */ + msre_engine_variable_register(engine, + "ARGS_POST", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_args_post_generate, + VAR_CACHE, + PHASE_REQUEST_BODY + ); + + /* ARGS_POST_NAMES */ + msre_engine_variable_register(engine, + "ARGS_POST_NAMES", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_args_post_names_generate, + VAR_CACHE, + PHASE_REQUEST_BODY + ); + + /* AUTH_TYPE */ + msre_engine_variable_register(engine, + "AUTH_TYPE", + VAR_SIMPLE, + 0, 0, + NULL, + var_auth_type_generate, + VAR_CACHE, + PHASE_REQUEST_BODY + ); + + /* ENV */ + msre_engine_variable_register(engine, + "ENV", + VAR_LIST, + 0, 1, + var_env_validate, + var_env_generate, + VAR_DONT_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* FILES */ + msre_engine_variable_register(engine, + "FILES", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_files_generate, + VAR_CACHE, + PHASE_REQUEST_BODY + ); + + /* FILES_COMBINED_SIZE */ + msre_engine_variable_register(engine, + "FILES_COMBINED_SIZE", + VAR_LIST, + 0, 0, + NULL, + var_files_combined_size_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_BODY + ); + + /* FILES_NAMES */ + msre_engine_variable_register(engine, + "FILES_NAMES", + VAR_LIST, + 0, 0, + NULL, + var_files_names_generate, + VAR_CACHE, + PHASE_REQUEST_BODY + ); + + /* FILES_SIZES */ + msre_engine_variable_register(engine, + "FILES_SIZES", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_files_sizes_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_BODY + ); + + /* FILES_TMPNAMES */ + msre_engine_variable_register(engine, + "FILES_TMPNAMES", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_files_tmpnames_generate, + VAR_CACHE, + PHASE_REQUEST_BODY + ); + + /* GEO */ + msre_engine_variable_register(engine, + "GEO", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_geo_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* GLOBAL */ + msre_engine_variable_register(engine, + "GLOBAL", + VAR_LIST, + 1, 1, + var_generic_list_validate, + var_global_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* HIGHEST_SEVERITY */ + msre_engine_variable_register(engine, + "HIGHEST_SEVERITY", + VAR_SIMPLE, + 0, 0, + NULL, + var_highest_severity_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* IP */ + msre_engine_variable_register(engine, + "IP", + VAR_LIST, + 1, 1, + var_generic_list_validate, + var_ip_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* MATCHED_VAR */ + msre_engine_variable_register(engine, + "MATCHED_VAR", + VAR_SIMPLE, + 0, 0, + NULL, + var_matched_var_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* MATCHED_VAR_NAME */ + msre_engine_variable_register(engine, + "MATCHED_VAR_NAME", + VAR_SIMPLE, + 0, 0, + NULL, + var_matched_var_name_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* MODSEC_BUILD */ + msre_engine_variable_register(engine, + "MODSEC_BUILD", + VAR_SIMPLE, + 0, 0, + NULL, + var_modsec_build_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* MULTIPART_BOUNDARY_QUOTED */ + msre_engine_variable_register(engine, + "MULTIPART_BOUNDARY_QUOTED", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_boundary_quoted_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_BOUNDARY_WHITESPACE */ + msre_engine_variable_register(engine, + "MULTIPART_BOUNDARY_WHITESPACE", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_boundary_whitespace_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_DATA_AFTER */ + msre_engine_variable_register(engine, + "MULTIPART_DATA_AFTER", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_data_after_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_DATA_BEFORE */ + msre_engine_variable_register(engine, + "MULTIPART_DATA_BEFORE", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_data_before_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_HEADER_FOLDING */ + msre_engine_variable_register(engine, + "MULTIPART_HEADER_FOLDING", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_header_folding_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_CRLF_LINE */ + msre_engine_variable_register(engine, + "MULTIPART_CRLF_LINE", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_crlf_line_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_CRLF_LF_LINES */ + msre_engine_variable_register(engine, + "MULTIPART_CRLF_LF_LINES", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_crlf_lf_lines_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_LF_LINE */ + msre_engine_variable_register(engine, + "MULTIPART_LF_LINE", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_lf_line_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_MISSING_SEMICOLON */ + msre_engine_variable_register(engine, + "MULTIPART_MISSING_SEMICOLON", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_missing_semicolon_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_INVALID_QUOTING */ + msre_engine_variable_register(engine, + "MULTIPART_INVALID_QUOTING", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_invalid_quoting_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_INVALID_HEADER_FOLDING */ + msre_engine_variable_register(engine, + "MULTIPART_INVALID_HEADER_FOLDING", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_invalid_header_folding_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_FILE_LIMIT_EXCEEDED */ + msre_engine_variable_register(engine, + "MULTIPART_FILE_LIMIT_EXCEEDED", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_file_limit_exceeded_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_STRICT_ERROR */ + msre_engine_variable_register(engine, + "MULTIPART_STRICT_ERROR", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_strict_error_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_UNMATCHED_BOUNDARY */ + msre_engine_variable_register(engine, + "MULTIPART_UNMATCHED_BOUNDARY", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_unmatched_boundary_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* PATH_INFO */ + msre_engine_variable_register(engine, + "PATH_INFO", + VAR_SIMPLE, + 0, 0, + NULL, + var_path_info_generate, + VAR_CACHE, + PHASE_REQUEST_BODY + ); + + /* QUERY_STRING */ + msre_engine_variable_register(engine, + "QUERY_STRING", + VAR_SIMPLE, + 0, 0, + NULL, + var_query_string_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* REMOTE_ADDR */ + msre_engine_variable_register(engine, + "REMOTE_ADDR", + VAR_SIMPLE, + 0, 0, + NULL, + var_remote_addr_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* REMOTE_HOST */ + msre_engine_variable_register(engine, + "REMOTE_HOST", + VAR_SIMPLE, + 0, 0, + NULL, + var_remote_host_generate, + VAR_CACHE, + PHASE_REQUEST_BODY + ); + + /* REMOTE_PORT */ + msre_engine_variable_register(engine, + "REMOTE_PORT", + VAR_SIMPLE, + 0, 0, + NULL, + var_remote_port_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_BODY + ); + + /* REMOTE_USER */ + msre_engine_variable_register(engine, + "REMOTE_USER", + VAR_SIMPLE, + 0, 0, + NULL, + var_remote_user_generate, + VAR_CACHE, + PHASE_REQUEST_BODY + ); + + /* RESOURCE */ + msre_engine_variable_register(engine, + "RESOURCE", + VAR_LIST, + 1, 1, + var_generic_list_validate, + var_resource_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* REQBODY_PROCESSOR */ + msre_engine_variable_register(engine, + "REQBODY_PROCESSOR", + VAR_SIMPLE, + 0, 0, + NULL, + var_reqbody_processor_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_HEADERS + ); + + /* REQBODY_PROCESSOR_ERROR */ + msre_engine_variable_register(engine, + "REQBODY_PROCESSOR_ERROR", + VAR_SIMPLE, + 0, 0, + NULL, + var_reqbody_processor_error_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_BODY + ); + + /* REQBODY_PROCESSOR_ERROR_MSG */ + msre_engine_variable_register(engine, + "REQBODY_PROCESSOR_ERROR_MSG", + VAR_SIMPLE, + 0, 0, + NULL, + var_reqbody_processor_error_msg_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_BODY + ); + + /* REQUEST_BASENAME */ + msre_engine_variable_register(engine, + "REQUEST_BASENAME", + VAR_SIMPLE, + 0, 0, + NULL, + var_request_basename_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_HEADERS + ); + + /* REQUEST_BODY */ + msre_engine_variable_register(engine, + "REQUEST_BODY", + VAR_SIMPLE, + 0, 0, + NULL, + var_request_body_generate, + VAR_CACHE, + PHASE_REQUEST_BODY + ); + + /* REQUEST_COOKIES */ + msre_engine_variable_register(engine, + "REQUEST_COOKIES", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_request_cookies_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* REQUEST_COOKIES_NAMES */ + msre_engine_variable_register(engine, + "REQUEST_COOKIES_NAMES", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_request_cookies_names_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* REQUEST_FILENAME */ + msre_engine_variable_register(engine, + "REQUEST_FILENAME", + VAR_SIMPLE, + 0, 0, + NULL, + var_request_filename_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* REQUEST_HEADERS */ + msre_engine_variable_register(engine, + "REQUEST_HEADERS", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_request_headers_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* REQUEST_HEADERS_NAMES */ + msre_engine_variable_register(engine, + "REQUEST_HEADERS_NAMES", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_request_headers_names_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* REQUEST_LINE */ + msre_engine_variable_register(engine, + "REQUEST_LINE", + VAR_SIMPLE, + 0, 0, + NULL, + var_request_line_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* REQUEST_METHOD */ + msre_engine_variable_register(engine, + "REQUEST_METHOD", + VAR_SIMPLE, + 0, 0, + NULL, + var_request_method_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* REQUEST_PROTOCOL */ + msre_engine_variable_register(engine, + "REQUEST_PROTOCOL", + VAR_SIMPLE, + 0, 0, + NULL, + var_request_protocol_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* REQUEST_URI */ + msre_engine_variable_register(engine, + "REQUEST_URI", + VAR_SIMPLE, + 0, 0, + NULL, + var_request_uri_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_HEADERS + ); + + /* REQUEST_URI_RAW */ + msre_engine_variable_register(engine, + "REQUEST_URI_RAW", + VAR_SIMPLE, + 0, 0, + NULL, + var_request_uri_raw_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* RESPONSE_BODY */ + msre_engine_variable_register(engine, + "RESPONSE_BODY", + VAR_SIMPLE, + 0, 0, + NULL, + var_response_body_generate, + VAR_CACHE, + PHASE_RESPONSE_BODY + ); + + /* RESPONSE_CONTENT_LENGTH */ + msre_engine_variable_register(engine, + "RESPONSE_CONTENT_LENGTH", + VAR_SIMPLE, + 0, 0, + NULL, + var_response_content_length, + VAR_DONT_CACHE, /* temp copy */ + PHASE_RESPONSE_HEADERS + ); + + /* RESPONSE_CONTENT_TYPE */ + msre_engine_variable_register(engine, + "RESPONSE_CONTENT_TYPE", + VAR_SIMPLE, + 0, 0, + NULL, + var_response_content_type, + VAR_CACHE, + PHASE_RESPONSE_HEADERS + ); + + /* RESPONSE_HEADERS */ + msre_engine_variable_register(engine, + "RESPONSE_HEADERS", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_response_headers_generate, + VAR_CACHE, + PHASE_RESPONSE_HEADERS + ); + + /* RESPONSE_HEADERS_NAMES */ + msre_engine_variable_register(engine, + "RESPONSE_HEADERS_NAMES", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_response_headers_names_generate, + VAR_CACHE, + PHASE_RESPONSE_HEADERS + ); + + /* RESPONSE_PROTOCOL */ + msre_engine_variable_register(engine, + "RESPONSE_PROTOCOL", + VAR_SIMPLE, + 0, 0, + NULL, + var_response_protocol_generate, + VAR_CACHE, + PHASE_RESPONSE_HEADERS + ); + + /* RESPONSE_STATUS */ + msre_engine_variable_register(engine, + "RESPONSE_STATUS", + VAR_SIMPLE, + 0, 0, + NULL, + var_response_status_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_RESPONSE_HEADERS + ); + + /* RULE */ + msre_engine_variable_register(engine, + "RULE", + VAR_LIST, + 1, 1, + NULL, + var_rule_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_RESPONSE_HEADERS + ); + + /* SCRIPT_GID */ + msre_engine_variable_register(engine, + "SCRIPT_GID", + VAR_SIMPLE, + 0, 0, + NULL, + var_script_gid_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_BODY + ); + + /* SCRIPT_BASENAME */ + msre_engine_variable_register(engine, + "SCRIPT_BASENAME", + VAR_SIMPLE, + 0, 0, + NULL, + var_script_basename_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_BODY + ); + + /* SCRIPT_FILENAME */ + msre_engine_variable_register(engine, + "SCRIPT_FILENAME", + VAR_SIMPLE, + 0, 0, + NULL, + var_script_filename_generate, + VAR_CACHE, + PHASE_REQUEST_BODY + ); + + /* SCRIPT_GROUPNAME */ + msre_engine_variable_register(engine, + "SCRIPT_GROUPNAME", + VAR_SIMPLE, + 0, 0, + NULL, + var_script_groupname_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_BODY + ); + + /* SCRIPT_MODE */ + msre_engine_variable_register(engine, + "SCRIPT_MODE", + VAR_SIMPLE, + 0, 0, + NULL, + var_script_mode_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_BODY + ); + + /* SCRIPT_UID */ + msre_engine_variable_register(engine, + "SCRIPT_UID", + VAR_SIMPLE, + 0, 0, + NULL, + var_script_uid_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_BODY + ); + + /* SCRIPT_USERNAME */ + msre_engine_variable_register(engine, + "SCRIPT_USERNAME", + VAR_SIMPLE, + 0, 0, + NULL, + var_script_username_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_BODY + ); + + /* SERVER_ADDR */ + msre_engine_variable_register(engine, + "SERVER_ADDR", + VAR_SIMPLE, + 0, 0, + NULL, + var_server_addr_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* SERVER_NAME */ + msre_engine_variable_register(engine, + "SERVER_NAME", + VAR_SIMPLE, + 0, 0, + NULL, + var_server_name_generate, + VAR_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* SERVER_PORT */ + msre_engine_variable_register(engine, + "SERVER_PORT", + VAR_SIMPLE, + 0, 0, + NULL, + var_server_port_generate, + VAR_DONT_CACHE, /* temp copy */ + PHASE_REQUEST_HEADERS + ); + + /* SESSION */ + msre_engine_variable_register(engine, + "SESSION", + VAR_LIST, + 1, 1, + var_generic_list_validate, + var_session_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* SESSIONID */ + msre_engine_variable_register(engine, + "SESSIONID", + VAR_SIMPLE, + 0, 0, + NULL, + var_sessionid_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_RESPONSE_HEADERS + ); + + /* STATUS_LINE */ + msre_engine_variable_register(engine, + "STATUS_LINE", + VAR_SIMPLE, + 0, 0, + NULL, + var_status_line_generate, + VAR_CACHE, + PHASE_RESPONSE_HEADERS + ); + + /* USER */ + msre_engine_variable_register(engine, + "USER", + VAR_LIST, + 1, 1, + var_generic_list_validate, + var_user_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* USERID */ + msre_engine_variable_register(engine, + "USERID", + VAR_SIMPLE, + 0, 0, + NULL, + var_userid_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_RESPONSE_HEADERS + ); + + /* TIME */ + msre_engine_variable_register(engine, + "TIME", + VAR_SIMPLE, + 0, 0, + NULL, + var_time_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* TIME_DAY */ + msre_engine_variable_register(engine, + "TIME_DAY", + VAR_SIMPLE, + 0, 0, + NULL, + var_time_day_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* TIME_EPOCH */ + msre_engine_variable_register(engine, + "TIME_EPOCH", + VAR_SIMPLE, + 0, 0, + NULL, + var_time_epoch_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* TIME_HOUR */ + msre_engine_variable_register(engine, + "TIME_HOUR", + VAR_SIMPLE, + 0, 0, + NULL, + var_time_hour_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* TIME_MIN */ + msre_engine_variable_register(engine, + "TIME_MIN", + VAR_SIMPLE, + 0, 0, + NULL, + var_time_min_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* TIME_MON */ + msre_engine_variable_register(engine, + "TIME_MON", + VAR_SIMPLE, + 0, 0, + NULL, + var_time_mon_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* TIME_SEC */ + msre_engine_variable_register(engine, + "TIME_SEC", + VAR_SIMPLE, + 0, 0, + NULL, + var_time_sec_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* TIME_WDAY */ + msre_engine_variable_register(engine, + "TIME_WDAY", + VAR_SIMPLE, + 0, 0, + NULL, + var_time_wday_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* TIME_YEAR */ + msre_engine_variable_register(engine, + "TIME_YEAR", + VAR_SIMPLE, + 0, 0, + NULL, + var_time_year_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* TX */ + msre_engine_variable_register(engine, + "TX", + VAR_LIST, + 1, 1, + var_generic_list_validate, + var_tx_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* WEBAPPID */ + msre_engine_variable_register(engine, + "WEBAPPID", + VAR_SIMPLE, + 0, 0, + NULL, + var_webappid_generate, + VAR_DONT_CACHE, + PHASE_RESPONSE_HEADERS + ); + + /* WEBSERVER_ERROR_LOG */ + msre_engine_variable_register(engine, + "WEBSERVER_ERROR_LOG", + VAR_LIST, + 0, 0, + NULL, + var_webserver_error_log_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_HEADERS + ); + + /* XML */ + msre_engine_variable_register(engine, + "XML", + VAR_LIST, + 0, 1, + var_xml_validate, + var_xml_generate, + VAR_DONT_CACHE, /* dynamic */ + PHASE_REQUEST_BODY + ); +} diff --git a/2.5.13/2.5.x/apache2/t/action/.empty b/2.5.13/2.5.x/apache2/t/action/.empty new file mode 100644 index 00000000..e69de29b diff --git a/2.5.13/2.5.x/apache2/t/csv_rx-pm.pl.in b/2.5.13/2.5.x/apache2/t/csv_rx-pm.pl.in new file mode 100755 index 00000000..6ea02ce8 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/csv_rx-pm.pl.in @@ -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} +}; diff --git a/2.5.13/2.5.x/apache2/t/gen_rx-pm.pl.in b/2.5.13/2.5.x/apache2/t/gen_rx-pm.pl.in new file mode 100755 index 00000000..c5e56adb --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/gen_rx-pm.pl.in @@ -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; +} diff --git a/2.5.13/2.5.x/apache2/t/op/beginsWith.t b/2.5.13/2.5.x/apache2/t/op/beginsWith.t new file mode 100644 index 00000000..831c00b0 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/beginsWith.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/contains.t b/2.5.13/2.5.x/apache2/t/op/contains.t new file mode 100644 index 00000000..0b406b48 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/contains.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/containsWord.t b/2.5.13/2.5.x/apache2/t/op/containsWord.t new file mode 100644 index 00000000..5b66bb5a --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/containsWord.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/endsWith.t b/2.5.13/2.5.x/apache2/t/op/endsWith.t new file mode 100644 index 00000000..d1b37629 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/endsWith.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/eq.t b/2.5.13/2.5.x/apache2/t/op/eq.t new file mode 100644 index 00000000..b8f2068c --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/eq.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/ge.t b/2.5.13/2.5.x/apache2/t/op/ge.t new file mode 100644 index 00000000..899c82a0 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/ge.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/geoLookup.t b/2.5.13/2.5.x/apache2/t/op/geoLookup.t new file mode 100644 index 00000000..716d5e68 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/geoLookup.t @@ -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, +}, + diff --git a/2.5.13/2.5.x/apache2/t/op/gt.t b/2.5.13/2.5.x/apache2/t/op/gt.t new file mode 100644 index 00000000..168d81f1 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/gt.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/inspectFile.t b/2.5.13/2.5.x/apache2/t/op/inspectFile.t new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/inspectFile.t @@ -0,0 +1 @@ + diff --git a/2.5.13/2.5.x/apache2/t/op/le.t b/2.5.13/2.5.x/apache2/t/op/le.t new file mode 100644 index 00000000..b6a84175 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/le.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/lt.t b/2.5.13/2.5.x/apache2/t/op/lt.t new file mode 100644 index 00000000..59882d46 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/lt.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/m.t b/2.5.13/2.5.x/apache2/t/op/m.t new file mode 100644 index 00000000..def6d08f --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/m.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/noMatch.t b/2.5.13/2.5.x/apache2/t/op/noMatch.t new file mode 100644 index 00000000..8775dd59 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/noMatch.t @@ -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, +}, + diff --git a/2.5.13/2.5.x/apache2/t/op/pm.t b/2.5.13/2.5.x/apache2/t/op/pm.t new file mode 100644 index 00000000..2850a811 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/pm.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/pmFromFile-01.dat b/2.5.13/2.5.x/apache2/t/op/pmFromFile-01.dat new file mode 100644 index 00000000..f7b07ea0 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/pmFromFile-01.dat @@ -0,0 +1,4 @@ +abc +def +ghi +xxx yyy zzz diff --git a/2.5.13/2.5.x/apache2/t/op/pmFromFile.t b/2.5.13/2.5.x/apache2/t/op/pmFromFile.t new file mode 100644 index 00000000..b565d86c --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/pmFromFile.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/rbl.t b/2.5.13/2.5.x/apache2/t/op/rbl.t new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/rbl.t @@ -0,0 +1 @@ + diff --git a/2.5.13/2.5.x/apache2/t/op/rx.t b/2.5.13/2.5.x/apache2/t/op/rx.t new file mode 100644 index 00000000..4be49e73 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/rx.t @@ -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, +}, + diff --git a/2.5.13/2.5.x/apache2/t/op/streq.t b/2.5.13/2.5.x/apache2/t/op/streq.t new file mode 100644 index 00000000..76451fe6 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/streq.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/unconditionalMatch.t b/2.5.13/2.5.x/apache2/t/op/unconditionalMatch.t new file mode 100644 index 00000000..56458d81 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/unconditionalMatch.t @@ -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, +}, + diff --git a/2.5.13/2.5.x/apache2/t/op/validateByteRange.t b/2.5.13/2.5.x/apache2/t/op/validateByteRange.t new file mode 100644 index 00000000..21a535e1 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/validateByteRange.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/validateDTD.t b/2.5.13/2.5.x/apache2/t/op/validateDTD.t new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/validateDTD.t @@ -0,0 +1 @@ + diff --git a/2.5.13/2.5.x/apache2/t/op/validateSchema.t b/2.5.13/2.5.x/apache2/t/op/validateSchema.t new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/validateSchema.t @@ -0,0 +1 @@ + diff --git a/2.5.13/2.5.x/apache2/t/op/validateUrlEncoding.t b/2.5.13/2.5.x/apache2/t/op/validateUrlEncoding.t new file mode 100644 index 00000000..904b1c36 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/validateUrlEncoding.t @@ -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, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/validateUtf8Encoding.t b/2.5.13/2.5.x/apache2/t/op/validateUtf8Encoding.t new file mode 100644 index 00000000..8c3825a6 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/validateUtf8Encoding.t @@ -0,0 +1,260 @@ +### Empty +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "", + ret => 0, +}, + +### Valid "I can eat glass and it does not hurt me." +# Greek +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει.", + ret => 0, +}, +# French +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Je peux manger du verre, ça ne me fait pas de mal.", + ret => 0, +}, +# Spanish +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Puedo comer vidrio, no me hace daño.", + ret => 0, +}, +# Esparanto +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Mi povas manĝi vitron, ĝi ne damaĝas min.", + ret => 0, +}, +# Latin +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Ic mæg glæs eotan ond hit ne hearmiað me.", + ret => 0, +}, +# Serbian +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Могу јести стакло а да ми не шкоди.", + ret => 0, +}, +# Russian +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Я могу есть стекло, оно мне не вредит.", + ret => 0, +}, +# Armenian +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Կրնամ ապակի ուտել և ինծի անհանգիստ չըներ։", + ret => 0, +}, +# Turkish +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "جام ييه بلورم بڭا ضررى طوقونمز", + ret => 0, +}, +# Hindi +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "मैं काँच खा सकता हूँ, मुझे उस से कोई पीडा नहीं होती.", + ret => 0, +}, +# Arabic +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "أنا قادر على أكل الزجاج و هذا لا يؤلمني.", + ret => 0, +}, +# Hebrew +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "אני יכול לאכול זכוכית וזה לא מזיק לי.", + ret => 0, +}, +# Japanese +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "私はガラスを食べられます。それは私を傷つけません。", + ret => 0, +}, +# Thai +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "ฉันกินกระจกได้ แต่มันไม่ทำให้ฉันเจ็บ", + ret => 0, +}, +# Korean +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "나는 유리를 먹을 수 있어요. 그래도 아프지 않아요", + ret => 0, +}, +# Navajo +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Tsésǫʼ yishą́ągo bííníshghah dóó doo shił neezgai da.", + ret => 0, +}, +# Icelandic +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Ég get etið gler án þess að meiða mig.", + ret => 0, +}, +# Sanskrit +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥", + ret => 0, +}, +# English Braille +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "⠊⠀⠉⠁⠝⠀⠑⠁⠞⠀⠛⠇⠁⠎⠎⠀⠁⠝⠙⠀⠊⠞⠀⠙⠕⠑⠎⠝⠞⠀⠓⠥⠗⠞⠀⠍⠑", + ret => 0, +}, +# Danish +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Jeg kan spise glas, det gør ikke ondt på mig.", + ret => 0, +}, +# Hungarian +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Meg tudom enni az üveget, nem lesz tőle bajom.", + ret => 0, +}, +# Estonian +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Ma võin klaasi süüa, see ei tee mulle midagi.", + ret => 0, +}, +# Czech +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Mohu jíst sklo, neublíží mi.", + ret => 0, +}, +# Slovak +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Môžem jesť sklo. Nezraní ma.", + ret => 0, +}, +# Polish +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "Mogę jeść szkło i mi nie szkodzi.", + ret => 0, +}, +# Symbols +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input=>"∮E⋅da=Qn→∞∑f(i)=∏g(i)∀x∈ℝ:⌈x⌉=−⌊−x⌋α∧¬β=¬(¬α∨β)ℕ⊆ℕ₀⊂ℤ⊂ℚ⊂ℝ⊂ℂ⊥ 0, +}, +### Invalid +# Umlauted a +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "\x00\xe4", + ret => 1, +}, +# Umlauted a +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "\xe4", + ret => 1, +}, +# +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "\x03\xbf", + ret => 1, +}, +# +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "\xc9\x3b", + ret => 1, +}, +### Invalid Full width +# +{ + type => "op", + name => "validateUtf8Encoding", + param => "", + input => "\xFF\x00", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/verifyCC.t b/2.5.13/2.5.x/apache2/t/op/verifyCC.t new file mode 100644 index 00000000..91162785 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/verifyCC.t @@ -0,0 +1,514 @@ +### Empty +# empty w/re not matching empty +{ + type => "op", + name => "verifyCC", + param => "\d+", + input => "", + ret => 0, +}, +# empty w/re matching empty +{ + type => "op", + name => "verifyCC", + param => '\d*', + input => "", + ret => 0, +}, + +### Non-matching +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "TestCase", + ret => 0, +}, + +### No digits in match +{ + type => "op", + name => "verifyCC", + param => 'TestCase', + input => "TestCase", + ret => 0, +}, + +### Too generic RE w/no matchs (Luhn will be called until all fail) +{ + type => "op", + name => "verifyCC", + param => '.*', + input => "TestCase", + ret => 0, +}, + +### Test Good CC# +# Mastercard +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "5484605089158216", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "5574407071707154", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "5351341509714210", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "5585166974020647", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "5492180332479256", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "5111178142162816", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "5511424748431031", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "5259964281562326", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "5138342589974385", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "5362069587634979", + ret => 1, +}, +# VISA 16 digit +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4916545704601136", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4539501231827691", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4556338049595394", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4929326438756024", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4485432027326322", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4532104980682081", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4485974616349298", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4916580487207199", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4532009746910413", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4024007144622932", + ret => 1, +}, +# VISA 13 digit +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4556324125126", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4067482954141", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4532402654980", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4539709679875", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "4024007182237", + ret => 1, +}, +# American Express +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "343918934573386", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "344881778330710", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "345439478558905", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "346465614421111", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "372263817755618", + ret => 1, +}, +# Discover +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "6011402777433576", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "6011890045362751", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "6011439091242416", + ret => 1, +}, +# Diners Club +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "30162519308318", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "30311556856867", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "36850112043985", + ret => 1, +}, +# enRoute +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "201427829075664", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "201434726660424", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "201453368666085", + ret => 1, +}, +# JCB 15 digit +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "210091499965007", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "210072739882947", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "180013970064072", + ret => 1, +}, +# JCB 16 digit +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "3096676276259096", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "3158726040010070", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "3096531217494742", + ret => 1, +}, +# Voyager +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "869974262335041", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "869905005856398", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "869950500085465", + ret => 1, +}, + +### Test Bad CC# +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d+)(?:[^\d]|$)', + input => "1234567890012345", + ret => 0, +}, + +### Test regex + Luhn +# from http://www.merriampark.com/anatomycc.htm +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "4417123456789113", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "4408041234567893", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "4408041234567890", + ret => 0, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "4417123456789112", + ret => 0, +}, +# on word boundary +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "a5484605089158216", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "a5484605089158216b", + ret => 1, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "5484605089158216b", + ret => 1, +}, +# valid patterns +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "5484-6050-8915-8216", + ret => 1, +}, +# changed digit from table above +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "5484605089158217", + ret => 0, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "5574407071807154", + ret => 0, +}, +# wrong patterns +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "5-484-6050-8915-8216", + ret => 0, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "5484 6050 8915 8216", + ret => 0, +}, +# not on digits boundary +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "15484605089158216", + ret => 0, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "154846050891582162", + ret => 0, +}, +{ + type => "op", + name => "verifyCC", + param => '(?:^|[^\d])(\d{4}\-?\d{4}\-?\d{2}\-?\d{2}\-?\d{1,4})(?:[^\d]|$)', + input => "54846050891582162", + ret => 0, +}, diff --git a/2.5.13/2.5.x/apache2/t/op/within.t b/2.5.13/2.5.x/apache2/t/op/within.t new file mode 100644 index 00000000..9d86dcdd --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/op/within.t @@ -0,0 +1,52 @@ +### Empty +{ + type => "op", + name => "within", + param => "", + input => "", + ret => 1, +}, +{ + type => "op", + name => "within", + param => "TestCase", + input => "", + ret => 1, +}, +{ + type => "op", + name => "within", + param => "", + input => "TestCase", + ret => 0, +}, + +### General +{ + type => "op", + name => "within", + param => "abcdefghi", + input => "abc", + ret => 1, +}, +{ + type => "op", + name => "within", + param => "abcdefghi", + input => "def", + ret => 1, +}, +{ + type => "op", + name => "within", + param => "abcdefghi", + input => "ghi", + ret => 1, +}, +{ + type => "op", + name => "within", + param => "abcdefghi", + input => "ghij", + ret => 0, +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/action/00-disruptive-actions.t b/2.5.13/2.5.x/apache2/t/regression/action/00-disruptive-actions.t new file mode 100644 index 00000000..c37133b1 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/action/00-disruptive-actions.t @@ -0,0 +1,531 @@ +### Tests all of the actions in each phase + +# Pass +{ + type => "action", + comment => "pass in phase:1", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:1,pass" + SecAction "phase:1,deny" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "pass in phase:2", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:2,pass" + SecAction "phase:2,deny" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "pass in phase:3", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecAction "phase:3,pass" + SecAction "phase:3,deny" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "pass in phase:4", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecAction "phase:4,pass" + SecAction "phase:4,deny" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# Allow +{ + type => "action", + comment => "allow in phase:1", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:1,allow" + SecAction "phase:1,deny" + ), + match_log => { + error => [ qr/ModSecurity: Access allowed \(phase 1\). Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "allow in phase:2", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:2,allow" + SecAction "phase:2,deny" + ), + match_log => { + error => [ qr/ModSecurity: Access allowed \(phase 2\). Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "allow in phase:3", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecAction "phase:3,allow" + SecAction "phase:3,deny" + ), + match_log => { + error => [ qr/ModSecurity: Access allowed \(phase 3\). Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "allow in phase:4", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecAction "phase:4,allow" + SecAction "phase:4,deny" + ), + match_log => { + error => [ qr/ModSecurity: Access allowed \(phase 4\). Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# Deny +{ + type => "action", + comment => "deny in phase:1", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:1,deny" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 1\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "deny in phase:2", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:2,deny" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 2\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "deny in phase:3", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecAction "phase:3,deny" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 3\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "deny in phase:4", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecAction "phase:4,deny" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 4\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# Drop +{ + type => "action", + comment => "drop in phase:1", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:1,drop" + ), + match_log => { + error => [ qr/Access denied with connection close \(phase 1\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "drop in phase:2", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:2,drop" + ), + match_log => { + error => [ qr/Access denied with connection close \(phase 2\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "drop in phase:3", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecAction "phase:3,drop" + ), + match_log => { + error => [ qr/Access denied with connection close \(phase 3\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "drop in phase:4", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecAction "phase:4,drop" + ), + match_log => { + error => [ qr/Access denied with connection close \(phase 4\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# Redirect +{ + type => "action", + comment => "redirect in phase:1 (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with redirection to .* using status 302 \(phase 1\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "redirect in phase:2 (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with redirection to .* using status 302 \(phase 2\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "redirect in phase:3 (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with redirection to .* using status 302 \(phase 3\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "redirect in phase:4 (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with redirection to .* using status 302 \(phase 4\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, + +# Proxy +{ + type => "action", + comment => "proxy in phase:1 (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied using proxy to \(phase 1\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "proxy in phase:2 (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied using proxy to \(phase 2\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "proxy in phase:3 (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 500 \(phase 3\) \(Configuration Error: Proxy action requested but it does not work in output phases\)./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "proxy in phase:4 (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 500 \(phase 4\) \(Configuration Error: Proxy action requested but it does not work in output phases\)./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/action/00-meta.t b/2.5.13/2.5.x/apache2/t/regression/action/00-meta.t new file mode 100644 index 00000000..9e7b92a8 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/action/00-meta.t @@ -0,0 +1,8 @@ +### Test meta actions + +# TODO: id +# TODO: logdata +# TODO: msg +# TODO: rev +# TODO: severity +# TODO: tag diff --git a/2.5.13/2.5.x/apache2/t/regression/action/00-misc.t b/2.5.13/2.5.x/apache2/t/regression/action/00-misc.t new file mode 100644 index 00000000..1629d697 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/action/00-misc.t @@ -0,0 +1,22 @@ +### Test misc actions + +# TODO: block +# TODO: capture +# TODO: chain +# TODO: deprecatevar +# TODO: exec +# TODO: expirevar +# TODO: initcol +# TODO: multiMatch +# TODO: pause +# TODO: sanitiseArg +# TODO: sanitiseMatched +# TODO: sanitiseRequestHeader +# TODO: sanitiseResponseHeader +# TODO: setuid +# TODO: setsid +# TODO: setenv +# TODO: setvar +# TODO: skip +# TODO: skipAfter +# TODO: xmlns diff --git a/2.5.13/2.5.x/apache2/t/regression/action/00-transformations.t b/2.5.13/2.5.x/apache2/t/regression/action/00-transformations.t new file mode 100644 index 00000000..e8f7bb2b --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/action/00-transformations.t @@ -0,0 +1,8 @@ +### Transformation tests + +# NOTE: individual tests done in unit tests + +# TODO: t:none to override default +# TODO: t:none inline +# TODO: combined +# TODO: caching diff --git a/2.5.13/2.5.x/apache2/t/regression/action/10-append-prepend.t b/2.5.13/2.5.x/apache2/t/regression/action/10-append-prepend.t new file mode 100644 index 00000000..97f28685 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/action/10-append-prepend.t @@ -0,0 +1,49 @@ +# TODO: Need more tests here + +### append +{ + type => "action", + comment => "append content", + conf => qq( + SecRuleEngine On + SecContentInjection On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAction "phase:1,setvar:tx.test=test" + SecAction "phase:2,append:'APPEND: \%{tx.test}'" + ), + match_log => { + debug => [ "Added content to bottom: APPEND: test", 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/APPEND: test$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +### prepend +{ + type => "action", + comment => "prepend content", + conf => qq( + SecRuleEngine On + SecContentInjection On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAction "phase:1,setvar:tx.test=test" + SecAction "phase:2,prepend:'PREPEND: \%{tx.test}'" + ), + match_log => { + debug => [ "Added content to top: PREPEND: test", 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^PREPEND: test/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/action/10-ctl.t b/2.5.13/2.5.x/apache2/t/regression/action/10-ctl.t new file mode 100644 index 00000000..1aa9fd15 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/action/10-ctl.t @@ -0,0 +1,56 @@ +### ctl + +### ruleRemoveById +{ + type => "action", + comment => "ruleRemoveById existing rule across phases", + conf => qq( + SecRuleEngine On + SecAction "phase:2,id:666,deny" + SecAction "phase:1,pass,ctl:ruleRemoveById=666" + ), + match_log => { + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "ruleRemoveById future rule across phases", + conf => qq( + SecRuleEngine On + SecAction "phase:1,pass,ctl:ruleRemoveById=666" + SecAction "phase:2,id:666,deny" + ), + match_log => { + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "ruleRemoveById future rule same phase", + conf => qq( + SecRuleEngine On + SecAction "phase:1,pass,ctl:ruleRemoveById=666" + SecAction "phase:1,id:666,deny" + ), + match_log => { + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + + diff --git a/2.5.13/2.5.x/apache2/t/regression/action/10-detectiononly-actions.t b/2.5.13/2.5.x/apache2/t/regression/action/10-detectiononly-actions.t new file mode 100644 index 00000000..238972c7 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/action/10-detectiononly-actions.t @@ -0,0 +1,545 @@ +### Tests all of the actions in each phase in detection only mode + +# Pass +{ + type => "action", + comment => "pass in phase:1", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAction "phase:1,pass,msg:'PASSED'" + SecAction "phase:1,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*PASSED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "pass in phase:2", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:2,pass,msg:'PASSED'" + SecAction "phase:2,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*PASSED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "pass in phase:3", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecAction "phase:3,pass,msg:'PASSED'" + SecAction "phase:3,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*PASSED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "pass in phase:4", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecAction "phase:4,pass,msg:'PASSED'" + SecAction "phase:4,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*PASSED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# Allow +{ + type => "action", + comment => "allow in phase:1", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:1,allow,msg:'ALLOWED'" + SecAction "phase:1,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ], + -error => [ qr/Access allowed/, 1 ], +# TODO: Allow should probably stop rule execution +# -error => [ qr/DENIED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "allow in phase:2", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:2,allow,msg:'ALLOWED'" + SecAction "phase:2,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ], + -error => [ qr/Access allowed/, 1 ], +# TODO: Allow should probably stop rule execution +# -error => [ qr/DENIED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "allow in phase:3", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:3,allow,msg:'ALLOWED'" + SecAction "phase:3,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ], + -error => [ qr/Access allowed/, 1 ], +# TODO: Allow should probably stop rule execution +# -error => [ qr/DENIED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "allow in phase:4", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:4,allow,msg:'ALLOWED'" + SecAction "phase:4,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ], + -error => [ qr/Access allowed/, 1 ], +# TODO: Allow should probably stop rule execution +# -error => [ qr/DENIED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# Deny +{ + type => "action", + comment => "deny in phase:1", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:1,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DENIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "deny in phase:2", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:2,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DENIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "deny in phase:3", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:3,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DENIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "deny in phase:4", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:4,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DENIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# Drop +{ + type => "action", + comment => "drop in phase:1", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:1,drop,msg:'DROPPED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DROPPED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "drop in phase:2", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:2,drop,msg:'DROPPED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DROPPED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "drop in phase:3", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:3,drop,msg:'DROPPED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DROPPED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "drop in phase:4", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:4,drop,msg:'DROPPED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DROPPED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# Redirect +{ + type => "action", + comment => "redirect in phase:1 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'REDIRECTED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*REDIRECTED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST 2$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "redirect in phase:2 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'REDIRECTED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*REDIRECTED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST 2$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "redirect in phase:3 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'REDIRECTED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*REDIRECTED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST 2$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "redirect in phase:4 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'REDIRECTED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*REDIRECTED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST 2$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, + +# Proxy +{ + type => "action", + comment => "proxy in phase:1 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'PROXIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*PROXIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST 2$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "proxy in phase:2 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'PROXIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*PROXIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST 2$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "proxy in phase:3 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'PROXIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*PROXIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "proxy in phase:4 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'PROXIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*PROXIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/action/10-logging.t b/2.5.13/2.5.x/apache2/t/regression/action/10-logging.t new file mode 100644 index 00000000..3c1d42b2 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/action/10-logging.t @@ -0,0 +1,564 @@ +### Logging tests + +# log/nolog (pass) +{ + type => "action", + comment => "log (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,log" + ), + match_log => { + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Warning\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "nolog (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# log/nolog (deny) +{ + type => "action", + comment => "log (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,log" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "nolog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# auditlog/noauditlog (pass) +{ + type => "action", + comment => "auditlog (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,auditlog" + ), + match_log => { + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Warning\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "noauditlog (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,noauditlog" + ), + match_log => { + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# auditlog/noauditlog (deny) +{ + type => "action", + comment => "auditlog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,auditlog" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "noauditlog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,noauditlog" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# All log/nolog auditlog/noauditlog combos (pass) +{ + type => "action", + comment => "log,auditlog (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,log,auditlog" + ), + match_log => { + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Warning\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "log,noauditlog (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,log,noauditlog" + ), + match_log => { + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "nolog,auditlog (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,nolog,auditlog" + ), + match_log => { + audit => [ qr/-H--\s+Message: .*Stopwatch: /s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "nolog,noauditlog (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,nolog,noauditlog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "auditlog,log (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,auditlog,log" + ), + match_log => { + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Warning\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "auditlog,nolog (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,auditlog,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "noauditlog,log (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,noauditlog,log" + ), + match_log => { + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "noauditlog,nolog (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,noauditlog,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# All log/nolog auditlog/noauditlog combos (deny) +{ + type => "action", + comment => "log,auditlog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,log,auditlog" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "log,noauditlog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,log,noauditlog" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "nolog,auditlog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,nolog,auditlog" + ), + match_log => { + audit => [ qr/-H--\s+Message: .*Stopwatch: /s, 1 ], + }, + match_response => { + -error => [ qr/ModSecurity: /, 1 ], + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "nolog,noauditlog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,nolog,noauditlog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "auditlog,log (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,auditlog,log" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "auditlog,nolog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,auditlog,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "noauditlog,log (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,noauditlog,log" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "noauditlog,nolog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,noauditlog,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/config/00-load-modsec.t b/2.5.13/2.5.x/apache2/t/regression/config/00-load-modsec.t new file mode 100644 index 00000000..9c6ecc5f --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/config/00-load-modsec.t @@ -0,0 +1,23 @@ +{ + type => "config", + comment => "module loaded", + match_log => { + error => [ qr/ModSecurity for Apache.* configured\./, 10 ], + }, +}, +{ + type => "config", + comment => "minimal config", + conf => sub { + # Open the minimal conf file, substituting the + # relative log paths with full paths. + open(C, "<$ENV{DIST_ROOT}/modsecurity.conf-minimal") or die "$!\n"; + (my $conf = join('', )) =~ s#Log logs/#Log $ENV{TEST_SERVER_ROOT}/logs/#g; + close C; + + return $conf; + }, + match_log => { + error => [ qr/ModSecurity for Apache.* configured\./, 10 ], + }, +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/config/10-audit-directives.t b/2.5.13/2.5.x/apache2/t/regression/config/10-audit-directives.t new file mode 100644 index 00000000..8a9d1663 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/config/10-audit-directives.t @@ -0,0 +1,264 @@ +### SecAudit* directive tests + +# SecAuditEngine +{ + type => "config", + comment => "SecAuditEngine On", + conf => qq( + SecAuditEngine On + SecAuditLog $ENV{AUDIT_LOG} + ), + match_log => { + audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecAuditEngine Off", + conf => qq( + SecAuditEngine Off + SecAuditLog $ENV{AUDIT_LOG} + ), + match_log => { + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecAuditEngine RelevantOnly (pos)", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog $ENV{AUDIT_LOG} + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecResponseBodyAccess On + SecDefaultAction "phase:2,log,auditlog,pass" + SecRule REQUEST_URI "." "phase:4,deny" + ), + match_log => { + audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecAuditEngine RelevantOnly (neg)", + conf => qq( + SecAuditEngine RelevantOnly + SecAuditLog $ENV{AUDIT_LOG} + SecResponseBodyAccess On + SecDefaultAction "phase:2,log,auditlog,pass" + ), + match_log => { + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecAuditLogType & SecAuditLogStorageDir +{ + type => "config", + comment => "SecAuditLogType Serial", + conf => qq( + SecAuditEngine On + SecAuditLog $ENV{AUDIT_LOG} + SecAuditLogType Serial + ), + match_log => { + audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^404$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/bogus", + ), +}, +{ + type => "config", + comment => "SecAuditLogType Concurrent", + conf => qq( + SecAuditEngine On + SecAuditLog $ENV{AUDIT_LOG} + SecAuditLogType Concurrent + SecAuditLogStorageDir "$ENV{LOGS_DIR}/audit" + ), + test => sub { + ### Perl code to parse the audit log entry and verify + ### that the concurrent audit log exists and contains + ### the correct data. + ### + ### TODO: Need some API for this :) + ### + + # Parse log + my $alogre = qr/^(?:\S+)\ (?:\S+)\ (?:\S+)\ (?:\S+)\ \[(?:[^:]+):(?:\d+:\d+:\d+)\ (?:[^\]]+)\]\ \"(?:.*)\"\ (?:\d+)\ (?:\S+)\ \"(?:.*)\"\ \"(?:.*)\"\ (\S+)\ \"(?:.*)\"\ (\S+)\ (?:\d+)\ (?:\d+)\ (?:\S+)(?:.*)$/m; + my $alog = match_log("audit", $alogre, 1); + chomp $alog; + my @log = ($alog =~ m/$alogre/); + my($id, $fn) = ($log[0], $log[1]); + if (!$id or !$fn) { + dbg("LOG ENTRY: $alog"); + die "Failed to parse audit log: $ENV{AUDIT_LOG}\n"; + } + + # Verify concurrent log exists + my $alogdatafn = "$ENV{LOGS_DIR}/audit$fn"; + if (! -e "$alogdatafn") { + die "Audit log does not exist: $alogdatafn\n"; + } + + # Verify concurrent log contents + if (defined match_file($alogdatafn, qr/^--[^-]+-A--.*$id.*-Z--$/s)) { + return 0; + } + + # Error + dbg("LOGDATA: \"$FILE{$alogdatafn}{buf}\""); + die "Audit log data did not match.\n"; + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecAuditLogRelevantStatus +{ + type => "config", + comment => "SecAuditLogRelevantStatus (pos)", + conf => qq( + SecAuditEngine RelevantOnly + SecAuditLog $ENV{AUDIT_LOG} + SecAuditLogRelevantStatus "^4" + ), + match_log => { + audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^404$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/bogus", + ), +}, +{ + type => "config", + comment => "SecAuditLogRelevantStatus (neg)", + conf => qq( + SecAuditEngine RelevantOnly + SecAuditLog $ENV{AUDIT_LOG} + SecAuditLogRelevantStatus "^4" + ), + match_log => { + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecAuditLogParts +{ + type => "config", + comment => "SecAuditLogParts (minimal)", + conf => qq( + SecAuditEngine On + SecAuditLog $ENV{AUDIT_LOG} + SecRequestBodyAccess On + SecResponseBodyAccess On + SecAuditLogParts "AZ" + ), + match_log => { + audit => [ qr/-A--.*-Z--/s, 1 ], + -audit => [ qr/-[B-Y]--/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1r&=2", + ), +}, +{ + type => "config", + comment => "SecAuditLogParts (default)", + conf => qq( + SecAuditEngine On + SecAuditLog $ENV{AUDIT_LOG} + SecRequestBodyAccess On + SecResponseBodyAccess On + ), + match_log => { + audit => [ qr/-A--.*-B--.*-F--.*-H--.*-Z--/s, 1 ], + -audit => [ qr/-[DEGIJK]--/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1r&=2", + ), +}, +{ + type => "config", + comment => "SecAuditLogParts (all)", + conf => qq( + SecRuleEngine On + SecAuditEngine On + SecAuditLog $ENV{AUDIT_LOG} + SecRequestBodyAccess On + SecResponseBodyAccess On + SecAuditLogParts "ABCDEFGHIJKZ" + SecAction "phase:4,log,auditlog,allow" + ), + match_log => { + audit => [ qr/-A--.*-B--.*-C--.*-F--.*-E--.*-H--.*-K--.*-Z--/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1r&=2", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/config/10-debug-directives.t b/2.5.13/2.5.x/apache2/t/regression/config/10-debug-directives.t new file mode 100644 index 00000000..b5376879 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/config/10-debug-directives.t @@ -0,0 +1,278 @@ +### SecDebug* directive tests +{ + type => "config", + comment => "SecDebugLog (pos)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + ), + match_log => { + debug => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecDebugLog (neg)", + conf => qq( + SecRuleEngine On + ), + match_log => { + -debug => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 0", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 0 + SecRule REQUEST_URI "." "phase:1,deny" + ), + match_log => { + -debug => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 1", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 1 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[[1]\] /, 1 ], + -debug => [ qr/\]\[[2-9]\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 2", + conf => qq( + SecRuleEngine DetectionOnly + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 2 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[2\] /, 1 ], + -debug => [ qr/\]\[[3-9]\] /, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 3", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 3 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[3\] /, 1 ], + -debug => [ qr/\]\[[4-9]\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 4", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 4 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[4\] /, 1 ], + -debug => [ qr/\]\[[5-9]\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 5", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 5 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[5\] /, 1 ], + -debug => [ qr/\]\[[6-9]\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 6", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 6 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[6\] /, 1 ], + -debug => [ qr/\]\[[7-9]\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 7", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 7 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[7\] /, 1 ], + -debug => [ qr/\]\[[8-9]\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 8", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 8 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[8\] /, 1 ], + -debug => [ qr/\]\[9\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 9", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[9\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/config/10-misc-directives.t b/2.5.13/2.5.x/apache2/t/regression/config/10-misc-directives.t new file mode 100644 index 00000000..0dffcaf1 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/config/10-misc-directives.t @@ -0,0 +1,148 @@ +### Misc directive tests + +### TODO: +# SecTmpDir +# SecUploadKeepFiles +# SecChrootDir +# SecGuardianLog + +# SecDefaultAction +{ + type => "config", + comment => "SecDefaultAction", + conf => qq( + SecRuleEngine on + SecDefaultAction "phase:1,deny,status:500" + SecRule REQUEST_URI "test.txt" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 500 \(phase 1\)/, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecServerSignature +{ + type => "config", + comment => "SecServerSignature On", + conf => qq( + SecServerSignature "NewServerSignature" + ), + match_log => { + error => [ qr/NewServerSignature/, 1 ], + }, + match_response => { + status => qr/^200$/, + raw => qr/^Server: +NewServerSignature$/m, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecDataDir +{ + type => "config", + comment => "SecDataDir", + conf => qq( + SecRuleEngine On + SecDataDir "$ENV{DATA_DIR}" + SecAction initcol:ip=%{REMOTE_ADDR},setvar:ip.dummy=1,pass + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + }, + match_file => { + "$ENV{DATA_DIR}/ip.pag" => qr/\x00\x06dummy\x00\x00\x021\x00/, + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecTmpDir/SecUploadDir/SecUploadKeepFiles +{ + type => "config", + comment => "SecTmpDir/SecUploadDir/SecUploadKeepFiles", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 4 + SecTmpDir "$ENV{TEMP_DIR}" + SecUploadKeepFiles On + SecUploadDir "$ENV{UPLOAD_DIR}" + ), + test => sub { + # Get the filename and make sure the file exists + my $fn = match_log(debug => qr/Moved file from .* to ".*"\./, 5); + die "Failed to determine uploaded filename\n" unless (defined $fn); + + $fn =~ s/Moved file from .* to "(.*)"\..*/$1/; + die "File does not exist: $fn\n" unless (-e $fn); + + # Check the contents of the file + return 0 if (match_file($fn, qr/^TESTFILE$/m)); + + msg("Failed to match contents of uploaded file: $fn"); + return 1; + }, + match_log => { + debug => [ qr/Created temporary file.*$ENV{TEMP_DIR}/, 1 ], + -debug => [ qr/Failed to /, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------19813181771830765643996187206", + ], + q(-----------------------------19813181771830765643996187206 +Content-Disposition: form-data; name="upload-file"; filename="test" +Content-Type: application/octet-stream + +TESTFILE +-----------------------------19813181771830765643996187206 +Content-Disposition: form-data; name="file" + +Upload File +-----------------------------19813181771830765643996187206--), + ), +}, + +# SecWebAppId +{ + type => "config", + comment => "SecWebAppId", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 4 + SecAuditLog "$ENV{AUDIT_LOG}" + SecAuditEngine RelevantOnly + SecWebAppId "app-1" + SecAction "pass,log,auditlog,id:1" + ), + match_log => { + error => [ qr/Warning\. Unconditional match in SecAction\./, 1 ], + debug => [ qr/Warning\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/^WebApp-Info: "app-1"/m, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/config/10-request-directives.t b/2.5.13/2.5.x/apache2/t/regression/config/10-request-directives.t new file mode 100644 index 00000000..16094ca7 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/config/10-request-directives.t @@ -0,0 +1,548 @@ +### Tests for directives altering how a request is handled + +# SecArgumentSeparator +{ + type => "config", + comment => "SecArgumentSeparator (get-pos)", + conf => q( + SecRuleEngine On + SecArgumentSeparator ";" + SecRule ARGS:a "@streq 1" "phase:1,deny,chain" + SecRule ARGS:b "@streq 2" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 1\)\. String match "2" at ARGS:b\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?a=1;b=2", + ), +}, +{ + type => "config", + comment => "SecArgumentSeparator (get-neg)", + conf => q( + SecRuleEngine On + SecRule ARGS:a "@streq 1" "phase:1,deny,chain" + SecRule ARGS:b "@streq 2" + ), + match_log => { + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?a=1;b=2", + ), +}, +{ + type => "config", + comment => "SecArgumentSeparator (post-pos)", + conf => q( + SecRuleEngine On + SecRequestBodyAccess On + SecArgumentSeparator ";" + SecRule ARGS:a "@streq 1" "phase:2,deny,chain" + SecRule ARGS:b "@streq 2" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 2\)\. String match "2" at ARGS:b\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1;b=2", + ), +}, +{ + type => "config", + comment => "SecArgumentSeparator (post-neg)", + conf => q( + SecRuleEngine On + SecRequestBodyAccess On + SecRule ARGS:a "@streq 1" "phase:2,deny" + SecRule ARGS:b "@streq 2" "phase:2,deny" + ), + match_log => { + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1;b=2", + ), +}, + +# SecRequestBodyAccess +{ + type => "config", + comment => "SecRequestBodyAccess (pos)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRule ARGS:a "\@streq 1" "phase:2,deny,chain" + SecRule ARGS:b "\@streq 2" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 2\)\. String match "2" at ARGS:b\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecRequestBodyAccess (neg)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess Off + SecRule ARGS:a "\@streq 1" "phase:2,deny" + SecRule ARGS:b "\@streq 2" "phase:2,deny" + ), + match_log => { + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, + +# SecRequestBodyLimit +{ + type => "config", + comment => "SecRequestBodyLimit (equal)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 7 + ), + match_log => { + -error => [ qr/Request body is larger than the configured limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (greater)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 5 + ), + match_log => { + error => [ qr/Request body .*is larger than the configured limit \(5\)\./, 1 ], + }, + match_response => { + status => qr/^413$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (equal - chunked)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 276 + ), + match_log => { + -error => [ qr/Request body is larger than the configured limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => normalize_raw_request_data( + qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked + + ), + ) + .encode_chunked( + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ) + ), + 1024 + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (greater - chunked)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 256 + ), + match_log => { + error => [ qr/Request body .*is larger than the configured limit \(256\)\./, 1 ], + }, + match_response => { + status => qr/^413$/, + }, + request => normalize_raw_request_data( + qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked + + ), + ) + .encode_chunked( + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ) + ), + 1024 + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (ctl:ruleEngine=off)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 5 + + SecAction "phase:1,pass,nolog,ctl:ruleEngine=off" + SecRule REQUEST_BODY "." "phase:2,deny" + ), + match_log => { + -error => [ qr/Request body .*is larger than the configured limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (ctl:requestBodyAccess=off)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 5 + + SecAction "phase:1,pass,nolog,ctl:requestBodyAccess=off" + SecRule REQUEST_BODY "." "phase:2,deny" + ), + match_log => { + -error => [ qr/Request body .*is larger than the configured limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (ctl:ruleEngine=off - chunked)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 256 + + SecAction "phase:1,pass,nolog,ctl:ruleEngine=off" + SecRule REQUEST_BODY "." "phase:2,deny" + ), + match_log => { + -error => [ qr/Request body .*is larger than the configured limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => normalize_raw_request_data( + qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked + + ), + ) + .encode_chunked( + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ) + ), + 1024 + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (ctl:requestBodyAccess=off - chunked)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 256 + + SecAction "phase:1,pass,nolog,ctl:requestBodyAccess=off" + SecRule REQUEST_BODY "." "phase:2,deny" + ), + match_log => { + -error => [ qr/Request body .*is larger than the configured limit \(256\)\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => normalize_raw_request_data( + qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked + + ), + ) + .encode_chunked( + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ) + ), + 1024 + ), +}, + +# SecRequestBodyInMemoryLimit +{ + type => "config", + comment => "SecRequestBodyInMemoryLimit (equal)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRequestBodyLimit 1000 + SecRequestBodyInMemoryLimit 276 + ), + match_log => { + -debug => [ qr/Input filter: Request too large to store in memory, switching to disk\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => normalize_raw_request_data( + qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked + + ), + ) + .encode_chunked( + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ) + ), + 1024 + ), +}, +{ + type => "config", + comment => "SecRequestBodyInMemoryLimit (greater)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRequestBodyLimit 1000 + SecRequestBodyInMemoryLimit 16 + ), + match_log => { + debug => [ qr/Input filter: Request too large to store in memory, switching to disk\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => normalize_raw_request_data( + qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked + + ), + ) + .encode_chunked( + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ) + ), + 1024 + ), +}, + +# SecCookieFormat +{ + type => "config", + comment => "SecCookieFormat (pos)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 5 + SecCookieFormat 1 + SecRule REQUEST_COOKIES_NAMES "\@streq SESSIONID" "phase:1,deny,chain" + SecRule REQUEST_COOKIES:\$SESSIONID_PATH "\@streq /" "chain" + SecRule REQUEST_COOKIES:SESSIONID "\@streq cookieval" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 1\)\. String match "cookieval" at REQUEST_COOKIES:SESSIONID\./, 1 ], + debug => [ qr(Adding request cookie: name "\$SESSIONID_PATH", value "/"), 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Cookie" => q($Version="1"; SESSIONID="cookieval"; $PATH="/"), + ], + undef, + ), +}, +{ + type => "config", + comment => "SecCookieFormat (neg)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 5 + SecCookieFormat 0 + SecRule REQUEST_COOKIES_NAMES "\@streq SESSIONID" "phase:1,deny,chain" + SecRule REQUEST_COOKIES:\$SESSIONID_PATH "\@streq /" "chain" + SecRule REQUEST_COOKIES:SESSIONID "\@streq cookieval" + ), + match_log => { + -error => [ qr/Access denied/, 1 ], + -debug => [ qr(Adding request cookie: name "\$SESSIONID_PATH", value "/"), 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Cookie" => q($Version="1"; SESSIONID="cookieval"; $PATH="/"), + ], + undef, + ), +}, + diff --git a/2.5.13/2.5.x/apache2/t/regression/config/10-response-directives.t b/2.5.13/2.5.x/apache2/t/regression/config/10-response-directives.t new file mode 100644 index 00000000..60617a29 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/config/10-response-directives.t @@ -0,0 +1,174 @@ +### Tests for directives altering how a response is handled + +# SecResponseBodyMimeTypesClear +{ + type => "config", + comment => "SecResponseBodyMimeTypesClear", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeTypesClear + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule RESPONSE_BODY "TEST" "phase:4,deny" + ), + match_log => { + -error => [ qr/Access denied/, 1 ], + debug => [ qr/Not buffering response body for unconfigured MIME type/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecResponseBodyAccess & SecResponseBodyMimeType +{ + type => "config", + comment => "SecResponseBodyAccess On", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecResponseBodyAccess On + SecResponseBodyMimeType text/plain null + SecRule RESPONSE_BODY "TEST" "phase:4,deny" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 4\)\. Pattern match "TEST" at RESPONSE_BODY\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecResponseBodyAccess Off", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecResponseBodyAccess Off + SecResponseBodyMimeType text/plain null + SecRule RESPONSE_BODY "TEST" "phase:4,deny" + ), + match_log => { + -error => [ qr/Access denied/, 1 ], + debug => [ qr/Response body buffering is not enabled\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecResponseBodyLimit +{ + type => "config", + comment => "SecResponseBodyLimit (equal)", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeType text/plain null + SecResponseBodyLimit 8192 + ), + match_log => { + -error => [ qr/Content-Length \(\d+\) over the limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt", + ), +}, +{ + type => "config", + comment => "SecResponseBodyLimit (less)", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeType text/plain null + SecResponseBodyLimit 9000 + ), + match_log => { + -error => [ qr/Content-Length \(\d+\) over the limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt", + ), +}, +{ + type => "config", + comment => "SecResponseBodyLimit (greater)", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeType text/plain null + SecResponseBodyLimit 8000 + ), + match_log => { + error => [ qr/Content-Length \(\d+\) over the limit \(8000\)\./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt", + ), +}, + +# ResponseBodyLimitAction +{ + type => "config", + comment => "SecResponseBodyLimitAction Reject", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeType text/plain null + SecResponseBodyLimit 5 + SecResponseBodyLimitAction Reject + ), + match_log => { + error => [ qr/Content-Length \(\d+\) over the limit \(5\)\./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt", + ), +}, +{ + type => "config", + comment => "SecResponseBodyLimitAction ProcessPartial", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeType text/plain null + SecResponseBodyLimit 5 + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 4 + SecResponseBodyLimitAction ProcessPartial + ), + match_log => { + -error => [ qr/Content-Length \(\d+\) over the limit/, 1 ], + debug => [ qr/Processing partial response body \(limit 5\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/config/20-chroot.t b/2.5.13/2.5.x/apache2/t/regression/config/20-chroot.t new file mode 100644 index 00000000..2147d2b6 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/config/20-chroot.t @@ -0,0 +1,35 @@ +### SecChroot tests +# TODO: Will not work as we need root access + +#{ +# type => "config", +# comment => "SecChroot", +# httpd_opts => qw( +# -DCHROOT +# ), +# conf => qq( +# # These will be in the chroot +# PidFile /logs/httpd.pid +# ScoreBoardFile /logs/httpd.scoreboard +# User nobody +# Group nogroup +# +# SecAuditEngine On +# SecDebugLog $ENV{DEBUG_LOG} +# SecDebugLogLevel 9 +# SecAuditLog $ENV{AUDIT_LOG} +# SecAuditLogStorageDir "/logs/audit" +# SecAuditLogType Concurrent +# SecChrootDir "$ENV{TEST_SERVER_ROOT}" +# ), +# match_log => { +# debug => [ qr/./, 1 ], +# audit => [ qr/./, 1 ], +# }, +# match_response => { +# status => qr/^200$/, +# }, +# request => new HTTP::Request( +# GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", +# ), +#}, diff --git a/2.5.13/2.5.x/apache2/t/regression/misc/00-multipart-parser.t b/2.5.13/2.5.x/apache2/t/regression/misc/00-multipart-parser.t new file mode 100644 index 00000000..3c58a291 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/misc/00-multipart-parser.t @@ -0,0 +1,1815 @@ +### Multipart parser tests + +# Normal +{ + type => "misc", + comment => "multipart parser (normal)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Added file part [0-9a-h]+ to the list: name "image" file name "image.jpg" \(offset 258, length 10\).*Adding request argument \(BODY\): name "name", value "Brian Rectanus".*Adding request argument \(BODY\): name "email", value "bpinto\@trustwave.com"/s, 1 ], + -debug => [ qr/Multipart.*(?i:error|warning)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + +# Final CRLF or not, we should still work +{ + type => "misc", + comment => "multipart parser (final CRLF)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ], + -debug => [ qr/Multipart error:/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, + +# No final CRLF +{ + type => "misc", + comment => "multipart parser (no final CRLF)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ], + -debug => [ qr/Multipart error:/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646--), + ), + ), +}, + +# Should work with a boundary of "boundary" +{ + type => "misc", + comment => "multipart parser (boundary contains \"boundary\")", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ], + -debug => [ qr/Multipart error:/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=------------------------------------------------boundary", + ], + normalize_raw_request_data( + q( + --------------------------------------------------boundary + Content-Disposition: form-data; name="a" + + 1 + --------------------------------------------------boundary + Content-Disposition: form-data; name="b" + + 2 + --------------------------------------------------boundary-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (boundary contains \"bOuNdArY\")", + note => q( + KHTML Boundary + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ], + -debug => [ qr/Multipart error:/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=--------0xKhTmLbOuNdArY", + ], + normalize_raw_request_data( + q( + ----------0xKhTmLbOuNdArY + Content-Disposition: form-data; name="a" + + 1 + ----------0xKhTmLbOuNdArY + Content-Disposition: form-data; name="b" + + 2 + ----------0xKhTmLbOuNdArY-- + ), + ), + ), +}, + +# We should handle data starting with a "--" +{ + type => "misc", + comment => "multipart parser (data contains \"--\")", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "a", value "--test".*Adding request argument \(BODY\): name "b", value "--"/s, 1 ], + -debug => [ qr/Multipart error:/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + --test + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + -- + -----------------------------69343412719991675451336310646--), + ), + ), +}, + +# We should emit warnings for parsing errors +{ + type => "misc", + comment => "multipart parser error (no final boundary)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecAuditLog "$ENV{AUDIT_LOG}" + SecAuditEngine RelevantOnly + ), + match_log => { + audit => [ qr/Final boundary missing/, 1 ], + debug => [ qr/Final boundary missing/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser error (no disposition)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecAuditLog "$ENV{AUDIT_LOG}" + SecAuditEngine RelevantOnly + ), + match_log => { + -debug => [ qr/Multipart error:/, 1 ], + audit => [ qr/Part missing Content-Disposition header/, 1 ], + debug => [ qr/Part missing Content-Disposition header/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + + 1 + -----------------------------69343412719991675451336310646 + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser error (bad disposition)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecAuditLog "$ENV{AUDIT_LOG}" + SecAuditEngine RelevantOnly + ), + match_log => { + audit => [ qr/Invalid Content-Disposition header/, 1 ], + debug => [ qr/Invalid Content-Disposition header/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser error (no disposition name)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecAuditLog "$ENV{AUDIT_LOG}" + SecAuditEngine RelevantOnly + ), + match_log => { + -debug => [ qr/Multipart error:/, 1 ], + audit => [ qr/Content-Disposition header missing name field/, 1 ], + debug => [ qr/Content-Disposition header missing name field/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, + +# Zero length part name should not crash +{ + type => "misc", + comment => "multipart parser (zero length part name)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "!\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*Invalid part header \(header name missing\)/s, 1 ], + -debug => [ qr/Adding request argument \(BODY\): name "b"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + : + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, + +# Part header folding +{ + type => "misc", + comment => "multipart parser (part header folding - space)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_INVALID_HEADER_FOLDING "!\@eq 0" "phase:2,deny,status:403" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:403" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*name: b.*variable: 2/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (part header folding - tab)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_INVALID_HEADER_FOLDING "!\@eq 0" "phase:2,deny,status:403" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:403" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*name: b.*variable: 2/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (part header folding - mixed)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_INVALID_HEADER_FOLDING "!\@eq 0" "phase:2,deny,status:403" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:403" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*name: b.*variable: 2/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (part header folding - invalid)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_INVALID_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:403" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*name: b.*variable: 2/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (part header folding - mixed invalid)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_INVALID_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:403" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*name: b.*variable: 2/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, + +# Data following final boundary should set flag +{ + type => "misc", + comment => "multipart parser (data after final boundary)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_DATA_AFTER "\@eq 1" "phase:2,deny,status:403" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*Ignoring data after last boundary/s, 1 ], + -debug => [ qr/Adding request argument \(BODY\): name "b"/s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646-- + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, + +# Single quoted data is invalid +{ + type => "misc", + comment => "multipart parser (C-D uses single quotes)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny" + SecRule MULTIPART_INVALID_QUOTING "!\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "chain,phase:2,deny" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*Duplicate Content-Disposition name/s, 1 ], + -debug => [ qr/Adding request argument \(BODY\): name "b/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name=';filename="dummy';name=b;" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, + +# Invalid boundary separators +{ + type => "misc", + comment => "multipart parser (invalid C-T boundary separator - comma)", + note => q( + PHP 5.2.3 - no effect. + ), + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + error => [ qr/Invalid boundary in C-T \(malformed\)/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data, boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (invalid C-T boundary separator - space)", + note => q( + PHP 5.2.3 - no effect. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + +# Invalid boundary parameter names +{ + type => "misc", + comment => "multipart parser (invalid C-T boundary parameter name - case)", + note => q( + PHP 5.2.3 - does not recognise boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + error => [ qr/Invalid boundary in C-T \(case sensitivity\)/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; bOundAry=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (invalid C-T boundary parameter name - trailing chars)", + note => q( + PHP 5.2.3 - does not recognise boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + error => [ qr/Invalid boundary in C-T \(parameter name\)/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary123=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + +# Invalid boundaries +{ + type => "misc", + comment => "multipart parser (multiple C-T boundaries - first quoted)", + note => q( + PHP 5.2.3 - uses first boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Multiple boundary parameters in C-T/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="0000"; boundary=1111), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (multiple C-T boundaries - comma separated)", + note => q( + PHP 5.2.3 - uses first boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Multiple boundary parameters in C-T/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000, boundary=1111), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (boundary whitespace in C-T - after name)", + note => q( + PHP 5.2.3 - no effect. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary =0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (boundary whitespace in C-T - before value)", + note => q( + PHP 5.2.3 - uses " 0000" as boundary. + We should probably interpret it as "0000" and just flag a strict error. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/boundary whitespace in C-T header/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary= 0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (boundary whitespace in C-T - after value)", + note => q( + PHP 5.2.3 - no effect. + Apache just silently removes the trailing whitespace for us. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Added file part [0-9a-h]+ to the list: name "image" file name "image.jpg" \(offset 258, length 10\).*Adding request argument \(BODY\): name "name", value "Brian Rectanus".*Adding request argument \(BODY\): name "email", value "brian.rectanus\@trustwave.com"/s, 1 ], + -debug => [ qr/Multipart.*(?i:error|warning)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000 ), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + +# Special chars +{ + type => "misc", + comment => "multipart parser (boundary special char - trailing whitespace+token)", + note => q( + PHP 5.2.3 - uses "0000 1111" as boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/No boundaries found in payload/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000 1111), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (boundary special char - trailing comma+token)", + note => q( + PHP 5.2.3 - uses "0000" as boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Invalid boundary in C-T \(characters\)/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000,1111), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + +# Quoting +{ + type => "misc", + comment => "multipart parser (quoted boundary - normal)", + note => q( + PHP 5.2.3 - no effect. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/boundary was quoted/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="0000"), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (quoted boundary value - whitespace before)", + note => q( + PHP 5.2.3 - uses " 0000" as boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/boundary was quoted.*No boundaries found in payload/s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=" 0000"), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (quoted boundary value - whitespace after)", + note => q( + PHP 5.2.3 - uses "0000 " as boundary. + Trailing whitespace violates RFC as well. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/boundary was quoted.*No boundaries found in payload/s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="0000 "), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (quoted boundary value - whitespace between)", + note => q( + PHP 5.2.3 - uses "0000 " as boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/boundary was quoted/s, 1 ], + -debug => [ qr/No boundaries found in payload/s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="0000 1111"), + ], + normalize_raw_request_data( + q( + --0000 1111 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 1111 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 1111 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000 1111-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (quoted boundary value - contained quote)", + note => q( + PHP 5.2.3 - uses "0000 " as boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Invalid boundary in C-T \(characters\)/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="00\"00"), + ], + normalize_raw_request_data( + q( + --00"00 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --00"00 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --00"00 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --00"00-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (quoted boundary value - two quoted values)", + note => q( + PHP 5.2.3 - does not work, uses "00" as boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Invalid boundary in C-T \(characters\)/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="00""00"), + ], + normalize_raw_request_data( + q( + --00"00 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --00"00 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --00"00 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --00"00-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (partial quoted boundary value - only start quote)", + note => q( + PHP 5.2.3 - breaks parsing. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Invalid boundary in C-T \(quote\)/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (partial quoted boundary value - only end quote)", + note => q( + PHP 5.2.3 - breaks parsing. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Invalid boundary in C-T \(quote\)/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000"), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + +# Multipart mixed +{ + type => "misc", + comment => "multipart parser (multipart mixed - normal)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Invalid Content-Disposition header/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: attachment + Content-Type: multipart/mixed; boundary=BbC04y + + --BbC04y + Content-Disposition: file; filename="file1.txt" + Content-Type: text/plain + + ... contents of file1.txt ... + --BbC04y + Content-Disposition: file; filename="file2.gif + Content-Type: image/jpeg + Content-Transfer-Encoding: binary + + ...contents of file2.gif... + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (multipart mixed - missing disposition)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Part missing Content-Disposition header/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Type: multipart/mixed; boundary=BbC04y + + --BbC04y + Content-Disposition: file; filename="file1.txt" + Content-Type: text/plain + + ... contents of file1.txt ... + --BbC04y + Content-Disposition: file; filename="file2.gif + Content-Type: image/jpeg + Content-Transfer-Encoding: binary + + ...contents of file2.gif... + --0000-- + ), + ), + ), +}, + +# File limits +{ + type => "misc", + comment => "multipart parser (normal)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecTmpDir "$ENV{TEMP_DIR}" + SecUploadDir "$ENV{UPLOAD_DIR}" + SecUploadKeepFiles On + SecUploadFileLimit 2 + + # These should be set + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny" + SecRule MULTIPART_FILE_LIMIT_EXCEEDED "!\@eq 1" "phase:2,deny" + + # This should not be set + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + + # Theses should still be accurate + SecRule &FILES "!\@eq 3" "phase:2,deny" + SecRule &FILES_NAMES "!\@eq 3" "phase:2,deny" + SecRule &FILES_SIZES "!\@eq 3" "phase:2,deny" + SecRule FILES_SIZES:/^image/ "\@eq 0" "phase:2,deny" + + # This should be the SecUploadFileLimit + SecRule &FILES_TMPNAMES "!\@eq 2" "phase:2,deny" + ), + match_log => { + debug => [ qr/Multipart: Upload file limit exceeded.*name: test.*variable: This is test data/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@trustwave.com + --0000 + Content-Disposition: form-data; name="image1"; filename="image1.jpg" + Content-Type: image/jpeg + + BINARYDATA1 + --0000 + Content-Disposition: form-data; name="image2"; filename="image2.jpg" + Content-Type: image/jpeg + + BINARYDATA2 + --0000 + Content-Disposition: form-data; name="image3"; filename="image3.jpg" + Content-Type: image/jpeg + + BINARYDATA3 + --0000 + Content-Disposition: form-data; name="test" + + This is test data. + --0000-- + ), + ), + ), +}, + diff --git a/2.5.13/2.5.x/apache2/t/regression/misc/00-phases.t b/2.5.13/2.5.x/apache2/t/regression/misc/00-phases.t new file mode 100644 index 00000000..97a40a52 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/misc/00-phases.t @@ -0,0 +1,151 @@ +### Test the phases + +# Phase 1 (request headers) +{ + type => "misc", + comment => "phase 1", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType text/plain null + SecRule REQUEST_LINE "^POST" "phase:1,pass,log,auditlog" + SecRule ARGS "val1" "phase:1,pass,log,auditlog" + SecRule RESPONSE_HEADERS:Last-Modified "." "phase:1,pass,log,auditlog" + SecRule RESPONSE_BODY "TEST" "phase:1,pass,log,auditlog" + ), + match_log => { + error => [ qr/Pattern match "\^POST" at REQUEST_LINE/, 1 ], + -error => [ qr/Pattern match .* (ARGS|RESPONSE)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# Phase 2 (request body) +{ + type => "misc", + comment => "phase 2", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType text/plain null + SecRule REQUEST_LINE "^POST" "phase:2,pass,log,auditlog" + SecRule ARGS "val1" "phase:2,pass,log,auditlog" + SecRule RESPONSE_HEADERS:Last-Modified "." "phase:2,pass,log,auditlog" + SecRule RESPONSE_BODY "TEST" "phase:2,pass,log,auditlog" + ), + match_log => { + error => [ qr/Pattern match "\^POST" at REQUEST_LINE.*Pattern match "val1" at ARGS/s, 1 ], + -error => [ qr/Pattern match .* RESPONSE/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# Phase 3 (response headers) +{ + type => "misc", + comment => "phase 3", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType text/plain null + SecRule REQUEST_LINE "^POST" "phase:3,pass,log,auditlog" + SecRule ARGS "val1" "phase:3,pass,log,auditlog" + SecRule RESPONSE_HEADERS:Last-Modified "." "phase:3,pass,log,auditlog" + SecRule RESPONSE_BODY "TEST" "phase:3,pass,log,auditlog" + ), + match_log => { + error => [ qr/Pattern match "\^POST" at REQUEST_LINE.*Pattern match "val1" at ARGS.*Pattern match "\." at RESPONSE_HEADERS/s, 1 ], + -error => [ qr/Pattern match .* RESPONSE_BODY/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# Phase 4 (response body) +{ + type => "misc", + comment => "phase 4", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType text/plain null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecRule REQUEST_LINE "^POST" "phase:4,pass,log,auditlog" + SecRule ARGS "val1" "phase:4,pass,log,auditlog" + SecRule RESPONSE_HEADERS:Last-Modified "." "phase:4,pass,log,auditlog" + SecRule RESPONSE_BODY "TEST" "phase:4,pass,log,auditlog" + ), + match_log => { + error => [ qr/Pattern match "\^POST" at REQUEST_LINE.*Pattern match "val1" at ARGS.*Pattern match "\." at RESPONSE_HEADERS.*Pattern match "TEST" at RESPONSE_BODY/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# Phase 5 (logging) +{ + type => "misc", + comment => "phase 5", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType text/plain null + SecRule REQUEST_LINE "^POST" "phase:5,pass,log,auditlog" + SecRule ARGS "val1" "phase:5,pass,log,auditlog" + SecRule RESPONSE_HEADERS:Last-Modified "." "phase:5,pass,log,auditlog" + SecRule RESPONSE_BODY "TEST" "phase:5,pass,log,auditlog" + ), + match_log => { + error => [ qr/Pattern match "\^POST" at REQUEST_LINE.*Pattern match "val1" at ARGS.*Pattern match "\." at RESPONSE_HEADERS.*Pattern match "TEST" at RESPONSE_BODY/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/misc/10-pcre.t b/2.5.13/2.5.x/apache2/t/regression/misc/10-pcre.t new file mode 100644 index 00000000..c66f4f9e --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/misc/10-pcre.t @@ -0,0 +1,38 @@ +### PCRE Limits + +{ + type => "misc", + comment => "PCRE limits", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + + SecRequestBodyAccess On + SecRequestBodyLimit 1000000 + SecRequestBodyInMemoryLimit 1000000 + + # Set Limits low + SecPcreMatchLimit 100 + SecPcreMatchLimitRecursion 100 + + # Poor REGEX + SecRule ARGS "(?:(.{2,})\\1{32,})" "phase:2,deny,capture,msg:'REDoS'" + # Detect PCRE limits exceeded + SecRule TX:MSC_PCRE_LIMITS_EXCEEDED "!\@streq 0" "phase:2,deny,msg:'ModSecurity Internal Error Flagged: %{MATCHED_VAR_NAME}'" + ), + match_log => { + debug => [ qr/PCRE limits exceeded/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + # Args + "test=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/misc/10-tfn-cache.t b/2.5.13/2.5.x/apache2/t/regression/misc/10-tfn-cache.t new file mode 100644 index 00000000..271834db --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/misc/10-tfn-cache.t @@ -0,0 +1,189 @@ +### Transformation Caching + +{ + type => "misc", + comment => "tfncache (simple fully cached)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + + # We need to make this work no matter what the defaults may change to + SecCacheTransformations On "minlen:1,maxlen:0" + + # This should cache it + SecRule ARGS_GET "WillNotMatch" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + + # This should use the cached value + SecRule ARGS_GET:test "foobar" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,deny" + ), + match_log => { + debug => [ qr/removeWhiteSpace,lowercase: "foobar" .*cached/, 1 ], + -debug => [ qr/partially cached/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html?test=Foo+Bar", + ), +}, +{ + type => "misc", + comment => "tfncache (simple partially cached)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + + # We need to make this work no matter what the defaults may change to + SecCacheTransformations On "minlen:1,maxlen:0,incremental:off,maxitems:0" + + # This should cache it + SecRule ARGS_GET "WillNotMatch" "phase:1,t:none,t:removeWhiteSpace,pass,nolog" + + # This should use the partially cached value + SecRule ARGS_GET:test "foobar" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,deny" + ), + match_log => { + debug => [ qr/removeWhiteSpace: "FooBar" .*partially cached/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html?test=Foo+Bar", + ), +}, +{ + type => "misc", + comment => "tfncache (separate phases)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + + # We need to make this work no matter what the defaults may change to + SecCacheTransformations On "minlen:1,maxlen:0" + + # This should cache it + SecRule ARGS_GET "WillNotMatch" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + + # This should use the cached value + SecRule ARGS_GET:test "foobar" "phase:2,t:none,t:removeWhiteSpace,t:lowercase,deny" + ), + match_log => { + -debug => [ qr/removeWhiteSpace,lowercase: "foobar" .*cached/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html?test=Foo+Bar", + ), +}, +{ + type => "misc", + comment => "tfncache (non-modifying tfns cached)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + + # We need to make this work no matter what the defaults may change to + SecCacheTransformations On "minlen:1,maxlen:0" + + # This should cache it + SecRule ARGS_GET "WillNotMatch" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + + # This should use the cached value + SecRule ARGS_GET:test "foobar" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,deny" + ), + match_log => { + debug => [ qr/removeWhiteSpace,lowercase: "foobar" .*cached/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html?test=foo+bar", + ), +}, +{ + type => "misc", + comment => "tfncache (unique keys)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + + # We need to make this work no matter what the defaults may change to + SecCacheTransformations On "minlen:1,maxlen:0" + + # This should cache it + SecRule ARGS "WillNotMatch" "phase:2,t:none,t:removeWhiteSpace,t:lowercase,pass" + + # This should see cached versions of *both* ARGS_GET + SecRule ARGS:test "queryval" "phase:2,t:none,t:removeWhiteSpace,t:lowercase,deny,chain" + SecRule ARGS:test "firstval" "t:none,t:removeWhiteSpace,t:lowercase,chain" + SecRule ARGS:test "secondval" "t:none,t:removeWhiteSpace,t:lowercase" + ), + match_log => { + debug => [ qr/removeWhiteSpace,lowercase: "queryval" .*removeWhiteSpace,lowercase: "firstval" .*cached.*removeWhiteSpace,lowercase: "secondval" .*cached/s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html?test=Query+Val", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + # Args + "test=First+Val&test=Second+Val", + ), +}, +{ + type => "misc", + comment => "tfncache (large cache)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + + + SecRequestBodyNoFilesLimit 1048576 + + SecRequestBodyInMemoryLimit 131072 + SecResponseBodyLimit 1048576 + + # We need to make this work no matter what the defaults may change to + SecCacheTransformations On "minlen:1,maxlen:0,maxitems:0" + + # This should cache it in all phases + SecRule ARGS "WillNotMatch" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + SecRule ARGS "WillNotMatch" "phase:2,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + SecRule ARGS "WillNotMatch" "phase:3,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + SecRule ARGS "WillNotMatch" "phase:4,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + + # This should use the cached value + SecRule ARGS "foobar" "phase:4,t:none,t:removeWhiteSpace,t:lowercase,deny" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "test", value "Foo Bar"/, 60, "Waiting for httpd to process request: "], + -error => [ qr/segmentation fault/i, 60 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + # 1000 Args + join("&", map { sprintf "arg%08d=0123456789abcdef+0123456789ABCDEF+0123456789abcdef", $_ } (1 .. 1000))."&test=Foo+Bar", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/misc/20-pdf-xss.t b/2.5.13/2.5.x/apache2/t/regression/misc/20-pdf-xss.t new file mode 100644 index 00000000..92d72303 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/misc/20-pdf-xss.t @@ -0,0 +1,54 @@ +# PDF XSS Protection + +{ + type => "misc", + comment => "pdf-xss - GET", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + + SecPdfProtect On + SecPdfProtectMethod TokenRedirection + SecPdfProtectSecret FooBar + SecPdfProtectTimeout 10 + ), + match_log => { + debug => [ qr/PdfProtect: PDF request without a token - redirecting to/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.pdf", + ), +}, +{ + type => "misc", + comment => "pdf-xss - POST", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + + SecPdfProtect On + SecPdfProtectMethod TokenRedirection + SecPdfProtectSecret FooBar + SecPdfProtectTimeout 10 + ), + match_log => { + -error => [ qr/exit signal/, 1 ], + debug => [ qr/PdfProtect: Not intercepting.*method=POST\/2/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.pdf", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + # Args + "a=1&b=2", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/rule/00-basics.t b/2.5.13/2.5.x/apache2/t/regression/rule/00-basics.t new file mode 100644 index 00000000..295bbc99 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/rule/00-basics.t @@ -0,0 +1,91 @@ +### Tests for basic rule components + +# SecAction +{ + type => "rule", + comment => "SecAction (override default)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 4 + SecAction "nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/Warning\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecRule +{ + type => "rule", + comment => "SecRule (no action)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 5 + SecDefaultAction "phase:2,deny,status:403" + SecRule ARGS:test "value" + ), + match_log => { + error => [ qr/ModSecurity: /, 1 ], + debug => [ qr/Rule [0-9a-f]+: SecRule "ARGS:test" "\@rx value" "phase:2,deny,status:403"$/m, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?test=value", + ), +}, +{ + type => "rule", + comment => "SecRule (action)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 5 + SecDefaultAction "phase:2,pass" + SecRule ARGS:test "value" "deny,status:403" + ), + match_log => { + error => [ qr/ModSecurity: /, 1 ], + debug => [ qr/Rule [0-9a-f]+: SecRule "ARGS:test" "\@rx value" "phase:2,deny,status:403"$/m, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?test=value", + ), +}, +{ + type => "rule", + comment => "SecRule (chain)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 5 + SecDefaultAction "phase:2,log,noauditlog,pass,tag:foo" + SecRule ARGS:test "value" "chain,phase:2,deny,status:403" + SecRule &ARGS "\@eq 1" "chain,setenv:tx.foo=bar" + SecRule REQUEST_METHOD "\@streq GET" + ), + match_log => { + error => [ qr/ModSecurity: /, 1 ], + debug => [ qr/Rule [0-9a-f]+: SecRule "ARGS:test" "\@rx value" "phase:2,log,noauditlog,tag:foo,chain,deny,status:403"\r?\n.*Rule [0-9a-f]+: SecRule "&ARGS" "\@eq 1" "chain,setenv:tx.foo=bar"\r?\n.*Rule [0-9a-f]+: SecRule "REQUEST_METHOD" "\@streq GET"\r?\n/s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?test=value", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/rule/00-inheritance.t b/2.5.13/2.5.x/apache2/t/regression/rule/00-inheritance.t new file mode 100644 index 00000000..c661c28e --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/rule/00-inheritance.t @@ -0,0 +1,4 @@ +### Tests for rule inheritance + +### TODO: +# SecRuleInheritance diff --git a/2.5.13/2.5.x/apache2/t/regression/rule/00-script.t b/2.5.13/2.5.x/apache2/t/regression/rule/00-script.t new file mode 100644 index 00000000..8af6d7bb --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/rule/00-script.t @@ -0,0 +1,63 @@ +### Test for SecRuleScript + +# Lua +{ + type => "rule", + comment => "SecRuleScript (lua absolute nomatch)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 1 + SecRuleScript "$ENV{CONF_DIR}/test.lua" "phase:2,deny" + ), + match_log => { + -error => [ qr/Lua script matched\./, 1 ], + debug => [ qr/Test message\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "SecRuleScript (lua relative nomatch)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 1 + SecRuleScript "test.lua" "phase:2,deny" + ), + match_log => { + -error => [ qr/Lua script matched\./, 1 ], + debug => [ qr/Test message\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "SecRuleScript (lua relative match)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 1 + SecRuleScript "match.lua" "phase:2,deny" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 2\)\. Lua script matched\./, 1 ], + debug => [ qr/Test message\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/rule/10-xml.t b/2.5.13/2.5.x/apache2/t/regression/rule/10-xml.t new file mode 100644 index 00000000..4ec937dd --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/rule/10-xml.t @@ -0,0 +1,419 @@ +### Test for XML operator rules + +### Validate Scheme +# OK +{ + type => "rule", + comment => "validateSchema (validate ok)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateSchema $ENV{CONF_DIR}/SoapEnvelope.xsd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*Successfully validated payload against Schema/s, 1 ], + -debug => [ qr/XML parser error|validation failed|Failed to load/, 1 ], + -error => [ qr/XML parser error|validation failed|Failed to load/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + 12123 + + + + ), + ), + ), +}, +# Failed attribute value +{ + type => "rule", + comment => "validateSchema (validate attribute value failed)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateSchema $ENV{CONF_DIR}/SoapEnvelope.xsd" \\ + "phase:2,deny,log,auditlog,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*'badval' is not a valid value of the local atomic type.*Schema validation failed/s, 1 ], + -debug => [ qr/Successfully validated payload against Schema|\n\r?\n/, 1 ], + audit => [ qr/^Message: Element.*'badval' is not a valid value of the local atomic type\.\nMessage:/m, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + 12123 + + + + ), + ), + ), +}, +# Failed validation +{ + type => "rule", + comment => "validateSchema (validate failed)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateSchema $ENV{CONF_DIR}/SoapEnvelope.xsd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*element is not expected/s, 1 ], + -debug => [ qr/XML parser error|Failed to load/, 1 ], + -error => [ qr/XML parser error|Failed to load/, 1 ], + audit => [ qr/^Message: Element.*This element is not expected.*\nMessage:/m, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + 12123 + + + + ), + ), + ), +}, +# Bad XML +{ + type => "rule", + comment => "validateSchema (bad XML)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateSchema $ENV{CONF_DIR}/SoapEnvelope.xsd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error.*validation failed because content is not well formed/s, 1 ], + -debug => [ qr/Failed to load|Successfully validated/, 1 ], + -error => [ qr/Failed to load|Successfully validated/, 1 ], + audit => [ qr/^Message: .*Failed parsing document.*\nMessage:/m, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + 12123 + + + + ), + ), + ), +}, +# Bad schema +{ + type => "rule", + comment => "validateSchema (bad schema)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateSchema $ENV{CONF_DIR}/SoapEnvelope-bad.xsd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*Failed to parse the XML resource.*Failed to load Schema/s, 1 ], + audit => [ qr/^Message: .*Failed to parse the XML resource.*\nMessage: Rule processing failed/m, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + 12123 + + + + ), + ), + ), +}, + +# Validate DTD +# OK +{ + type => "rule", + comment => "validateDTD (validate ok)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateDTD $ENV{CONF_DIR}/SoapEnvelope.dtd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*Successfully validated payload against DTD/s, 1 ], + -debug => [ qr/XML parser error|validation failed|Failed to load/, 1 ], + -error => [ qr/XML parser error|validation failed|Failed to load/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + + 12123 + + + + ), + ), + ), +}, +# Failed validation +{ + type => "rule", + comment => "validateDTD (validate failed)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateDTD $ENV{CONF_DIR}/SoapEnvelope.dtd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*content does not follow the DTD/s, 1 ], + -debug => [ qr/XML parser error|Failed to load/, 1 ], + -error => [ qr/XML parser error|Failed to load/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + + 12123 + + + + ), + ), + ), +}, +# Bad XML +{ + type => "rule", + comment => "validateDTD (bad XML)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateDTD $ENV{CONF_DIR}/SoapEnvelope.dtd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error.*validation failed because content is not well formed/s, 1 ], + -debug => [ qr/Failed to load|Successfully validated/, 1 ], + -error => [ qr/Failed to load|Successfully validated/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + + 12123 + + + + ), + ), + ), +}, +# Bad DTD +{ + type => "rule", + comment => "validateDTD (bad DTD)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateDTD $ENV{CONF_DIR}/SoapEnvelope-bad.dtd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*Failed to load DTD/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + + 12123 + + + + ), + ), + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/rule/20-exceptions.t b/2.5.13/2.5.x/apache2/t/regression/rule/20-exceptions.t new file mode 100644 index 00000000..12c058e9 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/rule/20-exceptions.t @@ -0,0 +1,176 @@ +### Tests for rule exceptions + +# SecRuleRemoveById +{ + type => "rule", + comment => "SecRuleRemoveById (single)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1" + SecRuleRemoveById 1 + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ], + -debug => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "SecRuleRemoveById (multiple)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:2" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:3" + SecRuleRemoveById 1 2 3 + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ], + -debug => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "SecRuleRemoveById (range)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:2" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:3" + SecRuleRemoveById 1-3 + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ], + -debug => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "SecRuleRemoveById (multiple + range)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:2" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:3" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:4" + SecRuleRemoveById 1 2-4 + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ], + -debug => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecRuleRemoveByMsg +{ + type => "rule", + comment => "SecRuleRemoveByMsg", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1,msg:'testing rule'" + SecRuleRemoveByMsg "testing rule" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ], + -debug => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecRuleUpdateActionById +{ + type => "rule", + comment => "SecRuleUpdateActionById", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1,msg:'testing rule'" + SecRuleUpdateActionById 1 "pass,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/id:1,.*,pass,nolog/, 1 ], + -debug => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "SecRuleUpdateActionById (chain)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1,msg:'testing rule',chain" + SecRule ARGS "bar" + SecRuleUpdateActionById 1 "pass,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/id:1,.*,pass,nolog/, 1 ], + -debug => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?foo=bar", + ), +}, diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope-bad.dtd b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope-bad.dtd new file mode 100644 index 00000000..7d6c19f4 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope-bad.dtd @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope-bad.xsd b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope-bad.xsd new file mode 100644 index 00000000..2acfd1da --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope-bad.xsd @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Prose in the spec does not specify that attributes are allowed on the Body element + + + + + + + + + + + + + + + + + + + + 'encodingStyle' indicates any canonicalization conventions followed in the contents of the containing element. For example, the value 'http://schemas.xmlsoap.org/soap/encoding/' indicates the pattern described in SOAP specification + + + + + + + + + + + + + + + Fault reporting structure + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope.dtd b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope.dtd new file mode 100644 index 00000000..0ad4a8ab --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope.dtd @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope.xsd b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope.xsd new file mode 100644 index 00000000..2b4a8c06 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/SoapEnvelope.xsd @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Prose in the spec does not specify that attributes are allowed on the Body element + + + + + + + + + + + + + + + + + + + + 'encodingStyle' indicates any canonicalization conventions followed in the contents of the containing element. For example, the value 'http://schemas.xmlsoap.org/soap/encoding/' indicates the pattern described in SOAP specification + + + + + + + + + + + + + + + Fault reporting structure + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/conf/httpd.conf.in b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/httpd.conf.in new file mode 100644 index 00000000..c24399ea --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/httpd.conf.in @@ -0,0 +1,37 @@ +### Base configuration for starting Apache httpd + + + # File locations + PidFile @MSC_REGRESSION_LOGS_DIR@/httpd.pid + ScoreBoardFile @MSC_REGRESSION_LOGS_DIR@/httpd.scoreboard + + + + LoadModule proxy_module @APXS_LIBEXECDIR@/mod_proxy.so + LoadModule proxy_http_module @APXS_LIBEXECDIR@/mod_proxy_http.so + + + LoadModule unique_id_module @APXS_LIBEXECDIR@/mod_unique_id.so + + + + # TODO: Need to have these configurable + LoadFile /usr/lib/libxml2.so + LoadFile /usr/lib/liblua5.1.so + LoadModule security2_module @APXS_LIBEXECDIR@/mod_security2.so + + +ServerName localhost + +CoreDumpDirectory @MSC_REGRESSION_SERVERROOT_DIR@/tmp + +LogLevel debug +ErrorLog @MSC_REGRESSION_LOGS_DIR@/error.log + + + DocumentRoot @MSC_REGRESSION_DOCROOT_DIR@ + + Options Indexes FollowSymLinks + AllowOverride None + + diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/conf/match.lua b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/match.lua new file mode 100644 index 00000000..fafd39b1 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/match.lua @@ -0,0 +1,14 @@ +-- Test matching Lua Script to just print debug messages +function main() + m.log(1, "Test message."); + m.log(2, "Test message."); + m.log(3, "Test message."); + m.log(4, "Test message."); + m.log(5, "Test message."); + m.log(6, "Test message."); + m.log(7, "Test message."); + m.log(8, "Test message."); + m.log(9, "Test message."); + + return "Lua script matched."; +end diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/conf/test.lua b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/test.lua new file mode 100644 index 00000000..1cff076d --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/server_root/conf/test.lua @@ -0,0 +1,14 @@ +-- Test Lua Script to just print debug messages +function main() + m.log(1, "Test message."); + m.log(2, "Test message."); + m.log(3, "Test message."); + m.log(4, "Test message."); + m.log(5, "Test message."); + m.log(6, "Test message."); + m.log(7, "Test message."); + m.log(8, "Test message."); + m.log(9, "Test message."); + + return nil; +end diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/data/.empty b/2.5.13/2.5.x/apache2/t/regression/server_root/data/.empty new file mode 100644 index 00000000..e69de29b diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/8k.txt b/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/8k.txt new file mode 100644 index 0000000000000000000000000000000000000000..6d17cf9d15fb9f4a2358a2d079f3b8c755d005fa GIT binary patch literal 8192 zcmeIu0Sy2E0K%a6Pi+o2h(KY$fB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM GyblZ@00031 literal 0 HcmV?d00001 diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/index.html b/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/index.html new file mode 100644 index 00000000..16c51c1e --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/index.html @@ -0,0 +1 @@ +INDEX diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/test.pdf b/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a6ec2d04c7ff2428885770f29ac8164264ab53c7 GIT binary patch literal 15406 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 B`Q`us literal 0 HcmV?d00001 diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/test.txt b/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/test.txt new file mode 100644 index 00000000..2a02d41c --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/test.txt @@ -0,0 +1 @@ +TEST diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/test2.txt b/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/test2.txt new file mode 100644 index 00000000..55d8fa4b --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/server_root/htdocs/test2.txt @@ -0,0 +1 @@ +TEST 2 diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/logs/audit/.empty b/2.5.13/2.5.x/apache2/t/regression/server_root/logs/audit/.empty new file mode 100644 index 00000000..e69de29b diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/logs/subdir/.empty b/2.5.13/2.5.x/apache2/t/regression/server_root/logs/subdir/.empty new file mode 100644 index 00000000..e69de29b diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/tmp/.empty b/2.5.13/2.5.x/apache2/t/regression/server_root/tmp/.empty new file mode 100644 index 00000000..e69de29b diff --git a/2.5.13/2.5.x/apache2/t/regression/server_root/upload/.empty b/2.5.13/2.5.x/apache2/t/regression/server_root/upload/.empty new file mode 100644 index 00000000..e69de29b diff --git a/2.5.13/2.5.x/apache2/t/regression/target/00-targets.t b/2.5.13/2.5.x/apache2/t/regression/target/00-targets.t new file mode 100644 index 00000000..d24f3777 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/regression/target/00-targets.t @@ -0,0 +1,577 @@ +### Test basic targets + +# ARGS +{ + type => "target", + comment => "ARGS (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS "val1" "phase:2,log,pass" + SecRule ARGS "val2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "val1" at ARGS.*Pattern match "val2" at ARGS/s, 1 ], + debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS "val1" "phase:2,log,pass" + SecRule ARGS "val2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "val1" at ARGS.*Pattern match "val2" at ARGS/s, 1 ], + debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# ARGS_COMBINED_SIZE +{ + type => "target", + comment => "ARGS_COMBINED_SIZE (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule ARGS_COMBINED_SIZE "\@eq 16" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Operator EQ matched 16 at ARGS_COMBINED_SIZE\./s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS_COMBINED_SIZE (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule ARGS_COMBINED_SIZE "\@eq 16" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Operator EQ matched 16 at ARGS_COMBINED_SIZE\./s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# ARGS_NAMES +{ + type => "target", + comment => "ARGS_NAMES (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_NAMES "arg1" "phase:2,log,pass" + SecRule ARGS_NAMES "arg2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "arg1" at ARGS.*Pattern match "arg2" at ARGS/s, 1 ], + debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS_NAMES (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_NAMES "arg1" "phase:2,log,pass" + SecRule ARGS_NAMES "arg2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "arg1" at ARGS_NAMES.*Pattern match "arg2" at ARGS_NAMES/s, 1 ], + debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# ARGS_GET +{ + type => "target", + comment => "ARGS_GET (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_GET "val1" "phase:2,log,pass" + SecRule ARGS_GET "val2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "val1" at ARGS_GET.*Pattern match "val2" at ARGS_GET/s, 1 ], + debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS_GET (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_GET "val1" "phase:2,log,pass" + SecRule ARGS_GET "val2" "phase:2,log,pass" + ), + match_log => { + -error => [ qr/Pattern match/, 1 ], + debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# ARGS_GET_NAMES +{ + type => "target", + comment => "ARGS_GET_NAMES (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_GET_NAMES "arg1" "phase:2,log,pass" + SecRule ARGS_GET_NAMES "arg2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "arg1" at ARGS_GET.*Pattern match "arg2" at ARGS_GET/s, 1 ], + debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS_GET_NAMES (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_GET_NAMES "arg1" "phase:2,log,pass" + SecRule ARGS_GET_NAMES "arg2" "phase:2,log,pass" + ), + match_log => { + -error => [ qr/Pattern match/, 1 ], + debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# ARGS_POST +{ + type => "target", + comment => "ARGS_POST (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_POST "val1" "phase:2,log,pass" + SecRule ARGS_POST "val2" "phase:2,log,pass" + ), + match_log => { + -error => [ qr/Pattern match/, 1 ], + debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS_POST (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_POST "val1" "phase:2,log,pass" + SecRule ARGS_POST "val2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "val1" at ARGS_POST.*Pattern match "val2" at ARGS_POST/s, 1 ], + debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# ARGS_POST_NAMES +{ + type => "target", + comment => "ARGS_POST_NAMES (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_POST_NAMES "arg1" "phase:2,log,pass" + SecRule ARGS_POST_NAMES "arg2" "phase:2,log,pass" + ), + match_log => { + -error => [ qr/Pattern match/, 1 ], + debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS_POST_NAMES (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_POST_NAMES "arg1" "phase:2,log,pass" + SecRule ARGS_POST_NAMES "arg2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "arg1" at ARGS_POST.*Pattern match "arg2" at ARGS_POST/s, 1 ], + debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# AUTH_TYPE +#{ +# type => "target", +# comment => "AUTH_TYPE", +# conf => qq( +# = 2.2> +# +# LoadModule authn_file_module modules/mod_authn_file.so +# +# +## +## +## LoadModule auth_module modules/mod_auth.so +## +## +# +# AuthType Basic +# AuthName Test +# AuthUserFile "$ENV{CONF_DIR}/htpasswd" +# Require user nobody +# +# SecRuleEngine On +# SecRequestBodyAccess On +# SecResponseBodyAccess On +# SecResponseBodyMimeType null +## SecDebugLog $ENV{DEBUG_LOG} +## SecDebugLogLevel 9 +# SecRule REQUEST_HEADERS:Authorization "Basic (.*)" "phase:2,log,pass,capture,chain" +# SecRule TX:1 "nobody:test" "t:none,t:base64Decode,chain" +# SecRule AUTH_TYPE "Basic" +# ), +# match_log => { +# error => [ qr/Pattern match "Basic" at AUTH_TYPE/s, 1 ], +# }, +# match_response => { +# status => qr/^200$/, +# }, +# request => new HTTP::Request( +# GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", +# [ +# "Authorization" => "Basic bm9ib2R5OnRlc3Q=" +# ], +# ), +#}, + +## ENH: We cannot include this test as we cannot distribute the database. +## Instead we should create a simple test DB of our own. +## GEO +#{ +# type => "target", +# comment => "GEO (ip)", +# conf => qq( +# SecRuleEngine On +# SecDebugLog $ENV{DEBUG_LOG} +# SecDebugLogLevel 9 +# SecGeoLookupDB GeoLiteCity.dat +# SecRule ARGS:ip "\@geoLookup" "phase:2,log,pass,t:none" +# SecRule GEO:COUNTRY_CODE "\@streq US" "phase:2,log,pass,t:none" +# SecRule GEO:COUNTRY_CODE3 "\@streq USA" "phase:2,log,pass,t:none" +# SecRule GEO:COUNTRY_NAME "\@streq United States" "phase:2,log,pass,t:none" +# # ENH: Not in this database? +# SecRule GEO:COUNTRY_CONTINENT "\@streq NA" "phase:2,log,pass,t:none" +# SecRule GEO:REGION "\@streq CA" "phase:2,log,pass,t:none" +# SecRule GEO:CITY "\@streq San Diego" "phase:2,log,pass,t:none" +# SecRule GEO:POSTAL_CODE "\@streq 92123" "phase:2,log,pass,t:none" +# SecRule GEO:LATITUDE "\@beginsWith 32.8" "phase:2,log,pass,t:none" +# SecRule GEO:LONGITUDE "\@beginsWith 117.1" "phase:2,log,pass,t:none" +# SecRule GEO:DMA_CODE "\@streq 825" "phase:2,log,pass,t:none" +# SecRule GEO:AREA_CODE "\@streq 858" "phase:2,log,pass,t:none" +# ), +# match_log => { +# debug => [ qr/Geo lookup for "216.75.21.122" succeeded.*match "US" at GEO:COUNTRY_CODE.*match "USA" at GEO:COUNTRY_CODE3.*match "United States" at GEO:COUNTRY_NAME.*match "NA" at GEO:COUNTRY_CONTINENT.*match "CA" at GEO:REGION.*match "San Diego" at GEO:CITY.*match "92123" at GEO:POSTAL_CODE.*match "32.8" at GEO:LATITUDE.*match "825" at GEO:DMA_CODE.*match "858" at GEO:AREA_CODE/si, 1 ], +# }, +# match_response => { +# status => qr/^200$/, +# }, +# request => new HTTP::Request( +# GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?ip=216.75.21.122", +# ), +#}, +#{ +# type => "target", +# comment => "GEO (host)", +# conf => qq( +# SecRuleEngine On +# SecDebugLog $ENV{DEBUG_LOG} +# SecDebugLogLevel 9 +# SecGeoLookupDB GeoLiteCity.dat +# SecRule ARGS:host "\@geoLookup" "phase:2,log,pass,t:none" +# SecRule GEO:COUNTRY_CODE "\@streq US" "phase:2,log,pass,t:none" +# SecRule GEO:COUNTRY_CODE3 "\@streq USA" "phase:2,log,pass,t:none" +# SecRule GEO:COUNTRY_NAME "\@streq United States" "phase:2,log,pass,t:none" +# # ENH: Not in this database? +# SecRule GEO:COUNTRY_CONTINENT "\@streq NA" "phase:2,log,pass,t:none" +# SecRule GEO:REGION "\@streq CA" "phase:2,log,pass,t:none" +# SecRule GEO:CITY "\@streq San Diego" "phase:2,log,pass,t:none" +# SecRule GEO:POSTAL_CODE "\@streq 92123" "phase:2,log,pass,t:none" +# SecRule GEO:LATITUDE "\@beginsWith 32.8" "phase:2,log,pass,t:none" +# SecRule GEO:LONGITUDE "\@beginsWith 117.1" "phase:2,log,pass,t:none" +# SecRule GEO:DMA_CODE "\@streq 825" "phase:2,log,pass,t:none" +# SecRule GEO:AREA_CODE "\@streq 858" "phase:2,log,pass,t:none" +# ), +# match_log => { +# debug => [ qr/Using address "\d+\.\d+\.\d+\.\d+".*Geo lookup for "www\.modsecurity\.org" succeeded.*match "US" at GEO:COUNTRY_CODE.*match "USA" at GEO:COUNTRY_CODE3.*match "United States" at GEO:COUNTRY_NAME.*match "NA" at GEO:COUNTRY_CONTINENT.*match "CA" at GEO:REGION.*match "San Diego" at GEO:CITY.*match "92123" at GEO:POSTAL_CODE.*match "32.8" at GEO:LATITUDE.*match "825" at GEO:DMA_CODE.*match "858" at GEO:AREA_CODE/si, 1 ], +# }, +# match_response => { +# status => qr/^200$/, +# }, +# request => new HTTP::Request( +# GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?host=www.modsecurity.org", +# ), +#}, +#{ +# type => "target", +# comment => "GEO (failed lookup)", +# conf => qq( +# SecRuleEngine On +# SecDebugLog $ENV{DEBUG_LOG} +# SecDebugLogLevel 9 +# SecGeoLookupDB GeoLiteCity.dat +# SecRule ARGS:ip "\@geoLookup" "phase:2,log,pass,t:none" +# SecRule \&GEO "\@eq 0" "phase:2,log,deny,status:403,t:none" +# SecRule ARGS:badip "\@geoLookup" "phase:2,log,pass,t:none" +# SecRule \&GEO "!\@eq 0" "phase:2,log,deny,status:403,t:none" +# ), +# match_log => { +# -debug => [ qr/Geo lookup for "127\.0\.0\.1" succeeded/si, 1 ], +# }, +# match_response => { +# status => qr/^200$/, +# }, +# request => new HTTP::Request( +# GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?ip=216.75.21.122&badip=127.0.0.1", +# ), +#}, + +# TODO: ENV +# TODO: FILES +# TODO: FILES_COMBINED_SIZE +# TODO: FILES_NAMES +# TODO: FILES_SIZES +# TODO: FILES_TMPNAMES +# TODO: HIGHEST_SEVERITY +# TODO: MATCHED_VAR +# TODO: MATCHED_VAR_NAME +# TODO: MODSEC_BUILD +# TODO: MULTIPART_CRLF_LF_LINES +# TODO: MULTIPART_STRICT_ERROR +# TODO: MULTIPART_UNMATCHED_BOUNDARY +# TODO: PATH_INFO +# TODO: QUERY_STRING +# TODO: REMOTE_ADDR +# TODO: REMOTE_HOST +# TODO: REMOTE_PORT +# TODO: REMOTE_USER +# TODO: REQBODY_PROCESSOR +# TODO: REQBODY_PROCESSOR_ERROR +# TODO: REQBODY_PROCESSOR_ERROR_MSG +# TODO: REQUEST_BASENAME +# TODO: REQUEST_BODY +# TODO: REQUEST_COOKIES +# TODO: REQUEST_COOKIES_NAMES +# TODO: REQUEST_FILENAME +# TODO: REQUEST_HEADERS +# TODO: REQUEST_HEADERS_NAMES +# TODO: REQUEST_LINE +# TODO: REQUEST_METHOD +# TODO: REQUEST_PROTOCOL +# TODO: REQUEST_URI +# TODO: REQUEST_URI_RAW +# TODO: RESPONSE_BODY +# TODO: RESPONSE_CONTENT_LENGTH +# TODO: RESPONSE_CONTENT_TYPE +# TODO: RESPONSE_HEADERS +# TODO: RESPONSE_HEADERS_NAMES +# TODO: RESPONSE_PROTOCOL +# TODO: RESPONSE_STATUS +# TODO: RULE +# TODO: SCRIPT_BASENAME +# TODO: SCRIPT_FILENAME +# TODO: SCRIPT_GID +# TODO: SCRIPT_GROUPNAME +# TODO: SCRIPT_MODE +# TODO: SCRIPT_UID +# TODO: SCRIPT_USERNAME +# TODO: SERVER_ADDR +# TODO: SERVER_NAME +# TODO: SERVER_PORT +# TODO: SESSION +# TODO: SESSIONID +# TODO: TIME +# TODO: TIME_DAY +# TODO: TIME_EPOCH +# TODO: TIME_HOUR +# TODO: TIME_MIN +# TODO: TIME_MON +# TODO: TIME_SEC +# TODO: TIME_WDAY +# TODO: TIME_YEAR +# TODO: TX +# TODO: USERID +# TODO: WEBAPPID +# TODO: WEBSERVER_ERROR_LOG +# TODO: XML + diff --git a/2.5.13/2.5.x/apache2/t/run-regression-tests.pl.in b/2.5.13/2.5.x/apache2/t/run-regression-tests.pl.in new file mode 100755 index 00000000..15614e94 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/run-regression-tests.pl.in @@ -0,0 +1,779 @@ +#!@PERL@ +# +# Run regression tests. +# +# Syntax: run-regression-tests.pl [options] [file [N]] +# +# All: run-regression-tests.pl +# All in file: run-regression-tests.pl file +# Nth in file: run-regression-tests.pl file N +# +use strict; +use Time::HiRes qw(gettimeofday sleep); +use POSIX qw(WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG); +use File::Spec qw(rel2abs); +use File::Basename qw(basename dirname); +use FileHandle; +use IPC::Open2 qw(open2); +use IPC::Open3 qw(open3); +use Getopt::Std; +use Data::Dumper; +use IO::Socket; +use LWP::UserAgent; + +my @TYPES = qw(config misc action target rule); +my $SCRIPT = basename($0); +my $SCRIPT_DIR = File::Spec->rel2abs(dirname($0)); +my $REG_DIR = "$SCRIPT_DIR/regression"; +my $SROOT_DIR = "$REG_DIR/server_root"; +my $DATA_DIR = "$SROOT_DIR/data"; +my $TEMP_DIR = "$SROOT_DIR/tmp"; +my $UPLOAD_DIR = "$SROOT_DIR/upload"; +my $CONF_DIR = "$SROOT_DIR/conf"; +my $MODULES_DIR = q(@APXS_LIBEXECDIR@); +my $FILES_DIR = "$SROOT_DIR/logs"; +my $PID_FILE = "$FILES_DIR/httpd.pid"; +my $HTTPD = q(@APXS_HTTPD@); +my $PASSED = 0; +my $TOTAL = 0; +my $BUFSIZ = 32768; +my %C = (); +my %FILE = (); +my $UA_NAME = "ModSecurity Regression Tests/1.2.3"; +my $UA = LWP::UserAgent->new; +$UA->agent($UA_NAME); + +# Hack for testing the script w/o configure +if ($HTTPD eq "\@APXS_HTTPD\@") { + $HTTPD = "/usr/local/apache2/bin/httpd"; + $MODULES_DIR = "/usr/local/apache2/modules"; +} + +$SIG{TERM} = $SIG{INT} = \&handle_interrupt; + +my %opt; +getopts('A:E:D:C:T:H:a:p:dvh', \%opt); + +if ($opt{d}) { + $Data::Dumper::Indent = 1; + $Data::Dumper::Terse = 1; + $Data::Dumper::Pad = ""; + $Data::Dumper::Quotekeys = 0; +} + +sub usage { + print stderr <<"EOT"; +@_ +Usage: $SCRIPT [options] [file [N]] + + Options: + -A file Specify ModSecurity audit log to read. + -D file Specify ModSecurity debug log to read. + -E file Specify Apache httpd error log to read. + -C file Specify Apache httpd base conf file to generate/reload. + -H path Specify Apache httpd htdocs path. + -S path Specify Apache httpd server root path. + -a file Specify Apache httpd binary (default: httpd) + -p port Specify Apache httpd port (default: 8088) + -v Enable verbose output (details on failure). + -d Enable debugging output. + -h This help. + +EOT + + exit(1); +} + +usage() if ($opt{h}); + +### Check httpd binary +if (defined $opt{a}) { + $HTTPD = $opt{a}; +} +else { + $opt{a} = $HTTPD; +} +usage("Invalid Apache startup script: $HTTPD\n") unless (-e $HTTPD); + +### Defaults +$opt{A} = "$FILES_DIR/modsec_audit.log" unless (defined $opt{A}); +$opt{D} = "$FILES_DIR/modsec_debug.log" unless (defined $opt{D}); +$opt{E} = "$FILES_DIR/error.log" unless (defined $opt{E}); +$opt{C} = "$CONF_DIR/httpd.conf" unless (defined $opt{C}); +$opt{H} = "$SROOT_DIR/htdocs" unless (defined $opt{H}); +$opt{p} = 8088 unless (defined $opt{p}); +$opt{v} = 1 if ($opt{d}); + +unless (defined $opt{S}) { + my $httpd_root = `$HTTPD -V`; + ($opt{S} = $httpd_root) =~ s/.*-D HTTPD_ROOT="([^"]*)".*/$1/sm; +} + +%ENV = ( + %ENV, + SERVER_ROOT => $opt{S}, + SERVER_PORT => $opt{p}, + SERVER_NAME => "localhost", + TEST_SERVER_ROOT => $SROOT_DIR, + DATA_DIR => $DATA_DIR, + TEMP_DIR => $TEMP_DIR, + UPLOAD_DIR => $UPLOAD_DIR, + CONF_DIR => $CONF_DIR, + MODULES_DIR => $MODULES_DIR, + LOGS_DIR => $FILES_DIR, + SCRIPT_DIR => $SCRIPT_DIR, + REGRESSION_DIR => $REG_DIR, + DIST_ROOT => File::Spec->rel2abs(dirname("$SCRIPT_DIR/../../..")), + AUDIT_LOG => $opt{A}, + DEBUG_LOG => $opt{D}, + ERROR_LOG => $opt{E}, + HTTPD_CONF => $opt{C}, + HTDOCS => $opt{H}, + USER_AGENT => $UA_NAME, +); + +#dbg("OPTIONS: ", \%opt); + +if (-e "$PID_FILE") { + msg("Shutting down previous instance: $PID_FILE"); + httpd_stop(); +} + +if (defined $ARGV[0]) { + runfile(dirname($ARGV[0]), basename($ARGV[0]), $ARGV[1]); + done(); +} + +for my $type (@TYPES) { + my $dir = "$SCRIPT_DIR/regression/$type"; + my @cfg = (); + + # Get test names + opendir(DIR, "$dir") or quit(1, "Failed to open \"$dir\": $!"); + @cfg = grep { /\.t$/ && -f "$dir/$_" } readdir(DIR); + closedir(DIR); + + for my $cfg (sort @cfg) { + runfile($dir, $cfg); + } +} +done(); + + +sub runfile { + my($dir, $cfg, $testnum) = @_; + my $fn = "$dir/$cfg"; + my @data = (); + my $edata; + my @C = (); + my @test = (); + my $teststr; + my $n = 0; + my $pass = 0; + + open(CFG, "<$fn") or quit(1, "Failed to open \"$fn\": $!"); + @data = ; + + $edata = q/@C = (/ . join("", @data) . q/)/; + eval $edata; + quit(1, "Failed to read test data \"$cfg\": $@") if ($@); + + unless (@C) { + msg("\nNo tests defined for $fn"); + return; + } + + msg("\nLoaded ".@C." tests from $fn"); + for my $t (@C) { + $n++; + next if (defined $testnum and $n != $testnum); + + my $httpd_up = 0; + my %t = %{$t || {}}; + my $id = sprintf("%3d", $n); + my $out = ""; + my $rc = 0; + my $conf_fn; + + # Startup httpd with optionally included conf. + if (exists $t{conf} and defined $t{conf}) { + $conf_fn = sprintf "%s/%s_%s_%06d.conf", + $CONF_DIR, $t{type}, $cfg, $n; + #dbg("Writing test config to: $conf_fn"); + open(CONF, ">$conf_fn") or die "Failed to open conf \"$conf_fn\": $!\n"; + print CONF (ref $t{conf} eq "CODE" ? eval { &{$t{conf}} } : $t{conf}); + msg("$@") if ($@); + close CONF; + $httpd_up = httpd_start(\%t, "Include $conf_fn") ? 0 : 1; + } + else { + $httpd_up = httpd_start(\%t) ? 0 : 1; + } + + # Run any prerun setup + if ($rc == 0 and exists $t{prerun} and defined $t{prerun}) { + vrb("Executing perl prerun..."); + $rc = &{$t{prerun}}; + vrb("Perl prerun returned: $rc"); + } + + if ($httpd_up) { + # Perform the request and check response + if (exists $t{request}) { + my $resp = do_request($t{request}); + if (!$resp) { + msg("invalid response"); + vrb("RESPONSE: ", $resp); + $rc = 1; + } + else { + for my $key (keys %{ $t{match_response} || {}}) { + my($neg,$mtype) = ($key =~ m/^(-?)(.*)$/); + my $m = $t{match_response}{$key}; + my $match = match_response($mtype, $resp, $m); + if ($neg and defined $match) { + $rc = 1; + msg("response $mtype matched: $m"); + vrb($resp); + last; + } + elsif (!$neg and !defined $match) { + $rc = 1; + msg("response $mtype failed to match: $m"); + vrb($resp); + last; + } + } + } + } + + # Run any arbitrary perl tests + if ($rc == 0 and exists $t{test} and defined $t{test}) { + dbg("Executing perl test(s)..."); + $rc = eval { &{$t{test}} }; + if (! defined $rc) { + msg("Error running test: $@"); + $rc = -1; + } + dbg("Perl tests returned: $rc"); + } + + # Search for all log matches + if ($rc == 0 and exists $t{match_log} and defined $t{match_log}) { + for my $key (keys %{ $t{match_log} || {}}) { + my($neg,$mtype) = ($key =~ m/^(-?)(.*)$/); + my $m = $t{match_log}{$key}; + my $match = match_log($mtype, @{$m || []}); + if ($neg and defined $match) { + $rc = 1; + msg("$mtype log matched: $m->[0]"); + last; + } + elsif (!$neg and !defined $match) { + $rc = 1; + msg("$mtype log failed to match: $m->[0]"); + last; + } + } + } + + # Search for all file matches + if ($rc == 0 and exists $t{match_file} and defined $t{match_file}) { + sleep 1; # Make sure the file exists + for my $key (keys %{ $t{match_file} || {}}) { + my($neg,$fn) = ($key =~ m/^(-?)(.*)$/); + my $m = $t{match_file}{$key}; + my $match = match_file($fn, $m); + if ($neg and defined $match) { + $rc = 1; + msg("$fn file matched: $m"); + last; + } + elsif (!$neg and !defined $match) { + $rc = 1; + msg("$fn file failed match: $m"); + last; + } + } + } + } + else { + msg("Failed to start httpd."); + $rc = 1; + } + + if ($rc == 0) { + $pass++; + } + else { + vrb("Test Config: $conf_fn"); + vrb("Debug Log: $FILE{debug}{fn}"); + dbg(escape("$FILE{debug}{buf}")); + vrb("Error Log: $FILE{error}{fn}"); + dbg(escape("$FILE{error}{buf}")); + } + + msg(sprintf("%s) %s%s: %s%s", $id, $t{type}, (exists($t{comment}) ? " - $t{comment}" : ""), ($rc ? "failed" : "passed"), ((defined($out) && $out ne "")? " ($out)" : ""))); + + if ($httpd_up) { + $httpd_up = httpd_stop(\%t) ? 0 : 1; + } + + } + + $TOTAL += $testnum ? 1 : $n; + $PASSED += $pass; + + msg(sprintf("Passed: %2d; Failed: %2d", $pass, $testnum ? (1 - $pass) : ($n - $pass))); +} + +# Take out any indenting and translate LF -> CRLF +sub normalize_raw_request_data { + my $r = $_[0]; + + # Allow for indenting in test file + $r =~ s/^[ \t]*\x0d?\x0a//s; + my($indention) = ($r =~ m/^([ \t]*)/s); # indention taken from first line + $r =~ s/^$indention//mg; + $r =~ s/(\x0d?\x0a)[ \t]+$/$1/s; + + # Translate LF to CRLF + $r =~ s/^\x0a/\x0d\x0a/mg; + $r =~ s/([^\x0d])\x0a/$1\x0d\x0a/mg; + + return $r; +} + +sub do_raw_request { + my $sock = new IO::Socket::INET( + Proto => "tcp", + PeerAddr => "localhost", + PeerPort => $opt{p}, + ) or msg("Failed to connect to localhost:$opt{p}: $@"); + return unless ($sock); + + # Join togeather the request + my $r = join("", @_); + dbg($r); + + # Write to socket + print $sock "$r"; + $sock->shutdown(1); + + # Read from socket + my @resp = <$sock>; + $sock->close(); + + return HTTP::Response->parse(join("", @resp)); +} + +sub do_request { + my $r = $_[0]; + + # Allow test to execute code + if (ref $r eq "CODE") { + $r = eval { &$r }; + msg("$@") unless (defined $r); + } + + if (ref $r eq "HTTP::Request") { + my $resp = $UA->request($r); + dbg($resp->request()->as_string()) if ($opt{d}); + return $resp + } + else { + return do_raw_request($r); + } + + return; +} + + +sub match_response { + my($name, $resp, $re) = @_; + + msg("Warning: Empty regular expression.") if (!defined $re or $re eq ""); + + if ($name eq "status") { + return $& if ($resp->code =~ m/$re/); + } + elsif ($name eq "content") { + return $& if ($resp->content =~ m/$re/m); + } + elsif ($name eq "raw") { + return $& if ($resp->as_string =~ m/$re/m); + } + + return; +} + +sub read_log { + my($name, $timeout, $graph) = @_; + return match_log($name, undef, $timeout, $graph); +} + +sub match_log { + my($name, $re, $timeout, $graph) = @_; + my $t0 = gettimeofday; + my($fh,$rbuf) = ($FILE{$name}{fd}, \$FILE{$name}{buf}); + my $n = length($$rbuf); + my $rc = undef; + + unless (defined $fh) { + msg("Error: File \"$name\" is not opened for matching."); + return; + } + + $timeout = 0 unless (defined $timeout); + + my $i = 0; + my $graphed = 0; + READ: { + do { + my $nbytes = $fh->sysread($$rbuf, $BUFSIZ, $n); + if (!defined($nbytes)) { + msg("Error: Could not read \"$name\" log: $!"); + last; + } + elsif (!defined($re) and $nbytes == 0) { + last; + } + + # Remove APR pool debugging + $$rbuf =~ s/POOL DEBUG:[^\n]+PALLOC[^\n]+\n//sg; + + $n = length($$rbuf); + + #dbg("Match \"$re\" in $name \"$$rbuf\" ($n)"); + if ($$rbuf =~ m/$re/m) { + $rc = $&; + last; + } + # TODO: Use select()/poll() + sleep 0.1 unless ($nbytes == $BUFSIZ); + if ($graph and $opt{d}) { + $i++; + if ($i == 10) { + $graphed++; + $i=0; + print STDERR $graph if ($graphed == 1); + print STDERR "." + } + } + } while (gettimeofday - $t0 < $timeout); + } + print STDERR "\n" if ($graphed); + + return $rc; +} + +sub match_file { + my($neg,$fn) = ($_[0] =~ m/^(-?)(.*)$/); + unless (exists $FILE{$fn}) { + eval { + $FILE{$fn}{fn} = $fn; + $FILE{$fn}{fd} = new FileHandle($fn, O_RDONLY) or die "$!\n"; + $FILE{$fn}{fd}->blocking(0); + $FILE{$fn}{buf} = ""; + }; + if ($@) { + msg("Warning: Failed to open file \"$fn\": $@"); + return; + } + } + return match_log($_[0], $_[1]); # timeout makes no sense +} + +sub quote_shell { + my($s) = @_; + return $s unless ($s =~ m|[^\w!%+,\-./:@^]|); + $s =~ s/(['\\])/\\$1/g; + return "'$s'"; +} + +sub escape { + my @new = (); + for my $c (split(//, $_[0])) { + my $oc = ord($c); + push @new, ((($oc >= 0x20 and $oc <= 0x7e) or $oc == 0x0a or $oc == 0x0d) ? $c : sprintf("\\x%02x", ord($c))); + } + join('', @new); +} + +sub dbg { + return unless(@_ and $opt{d}); + my $out = join "", map { + (ref $_ ne "" ? Dumper($_) : $_) + } @_; + $out =~ s/^/DBG: /mg; + print STDOUT "$out\n"; +} + +sub vrb { + return unless(@_ and $opt{v}); + msg(@_); +} + +sub msg { + return unless(@_); + my $out = join "", map { + (ref $_ ne "" ? Dumper($_) : $_) + } @_; + print STDOUT "$out\n"; +} + +sub handle_interrupt { + $SIG{TERM} = $SIG{INT} = \&handle_interrupt; + + msg("Interrupted via SIG$_[0]. Shutting down tests..."); + httpd_stop(); + + quit(1); +} + +sub quit { + my($ec,$msg) = @_; + $ec = 0 unless (defined $_[0]); + + msg("$msg") if (defined $msg); + + exit $ec; +} + +sub done { + if ($PASSED != $TOTAL) { + quit(1, "\n$PASSED/$TOTAL tests passed."); + } + + quit(0, "\nAll tests passed ($TOTAL)."); +} + +sub httpd_start { + my $t = shift; + httpd_reset_fd($t); + my @p = ( + $HTTPD, + -d => $opt{S}, + -f => $opt{C}, + (map { (-c => $_) } ("Listen localhost:$opt{p}", @_)), + -k => "start", + ); + + my $httpd_out; + my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1); + my $out = join("\\n", grep(!/POOL DEBUG/, (<$httpd_out>))); + close $httpd_out; + waitpid($httpd_pid, 0); + + my $rc = $?; + if ( WIFEXITED($rc) ) { + $rc = WEXITSTATUS($rc); + vrb("Httpd start returned with $rc.") if ($rc); + } + elsif( WIFSIGNALED($rc) ) { + msg("Httpd start failed with signal " . WTERMSIG($rc) . "."); + $rc = -1; + } + else { + msg("Httpd start failed with unknown error."); + $rc = -1; + } + + if (defined $out and $out ne "") { + vrb(join(" ", map { quote_shell($_) } @p)); + msg("Httpd start failed with error messages:\n$out"); + return -1 + } + + # Look for startup msg + unless (defined match_log("error", qr/resuming normal operations/, 60, "Waiting on httpd to start: ")) { + vrb(join(" ", map { quote_shell($_) } @p)); + vrb(match_log("error", qr/(^.*ModSecurity: .*)/sm, 10)); + msg("Httpd server failed to start."); + return -1; + } + + return $rc; +} + +sub httpd_stop { + my $t = shift; + my @p = ( + $HTTPD, + -d => $opt{S}, + -f => $opt{C}, + (map { (-c => $_) } ("Listen localhost:$opt{p}", @_)), + -k => "stop", + ); + + my $httpd_out; + my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1); + my $out = join("\\n", grep(!/POOL DEBUG/, (<$httpd_out>))); + close $httpd_out; + waitpid($httpd_pid, 0); + + if (defined $out and $out ne "") { + msg("Httpd stop failed with error messages:\n$out"); + return -1 + } + + my $rc = $?; + if ( WIFEXITED($rc) ) { + $rc = WEXITSTATUS($rc); + vrb("Httpd stop returned with $rc.") if ($rc); + } + elsif( WIFSIGNALED($rc) ) { + msg("Httpd stop failed with signal " . WTERMSIG($rc) . "."); + $rc = -1; + } + else { + msg("Httpd stop failed with unknown error."); + $rc = -1; + } + + # Look for startup msg + unless (defined match_log("error", qr/caught SIG[A-Z]+, shutting down/, 60, "Waiting on httpd to stop: ")) { + vrb(join(" ", map { quote_shell($_) } @p)); + msg("Httpd server failed to shutdown."); + sleep 0.5; + return -1; + } + + sleep 0.5; + + return $rc; +} + +sub httpd_reload { + my $t = shift; + httpd_reset_fd($t); + my @p = ( + $HTTPD, + -d => $opt{S}, + -f => $opt{C}, + (map { (-c => $_) } ("Listen localhost:$opt{p}", @_)), + -k => "graceful", + ); + + my $httpd_out; + my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1); + my $out = join("\\n", grep(!/POOL DEBUG/, (<$httpd_out>))); + close $httpd_out; + waitpid($httpd_pid, 0); + + if (defined $out and $out ne "") { + msg("Httpd reload failed with error messages:\n$out"); + return -1 + } + + my $rc = $?; + if ( WIFEXITED($rc) ) { + $rc = WEXITSTATUS($rc); + vrb("Httpd reload returned with $rc.") if ($rc); + } + elsif( WIFSIGNALED($rc) ) { + msg("Httpd reload failed with signal " . WTERMSIG($rc) . "."); + $rc = -1; + } + else { + msg("Httpd reload failed with unknown error."); + $rc = -1; + } + + # Look for startup msg + unless (defined match_log("error", qr/resuming normal operations/, 60, "Waiting on httpd to restart: ")) { + vrb(join(" ", map { quote_shell($_) } @p)); + msg("Httpd server failed to reload."); + return -1; + } + + return $rc; +} + +sub httpd_reset_fd { + my($t) = @_; + + # Cleanup + for my $key (keys %FILE) { + if (exists $FILE{$key}{fd} and defined $FILE{$key}{fd}) { + $FILE{$key}{fd}->close(); + } + delete $FILE{$key}; + } + + # Error + eval { + $FILE{error}{fn} = $opt{E}; + $FILE{error}{fd} = new FileHandle($opt{E}, O_RDWR|O_CREAT) or die "$!\n"; + $FILE{error}{fd}->blocking(0); + $FILE{error}{fd}->sysseek(0, 2); + $FILE{error}{buf} = ""; + }; + if ($@) { + msg("Warning: Failed to open file \"$opt{E}\": $@"); + return undef; + } + + # Audit + eval { + $FILE{audit}{fn} = $opt{A}; + $FILE{audit}{fd} = new FileHandle($opt{A}, O_RDWR|O_CREAT) or die "$!\n"; + $FILE{audit}{fd}->blocking(0); + $FILE{audit}{fd}->sysseek(0, 2); + $FILE{audit}{buf} = ""; + }; + if ($@) { + msg("Warning: Failed to open file \"$opt{A}\": $@"); + return undef; + } + + # Debug + eval { + $FILE{debug}{fn} = $opt{D}; + $FILE{debug}{fd} = new FileHandle($opt{D}, O_RDWR|O_CREAT) or die "$!\n"; + $FILE{debug}{fd}->blocking(0); + $FILE{debug}{fd}->sysseek(0, 2); + $FILE{debug}{buf} = ""; + }; + if ($@) { + msg("Warning: Failed to open file \"$opt{D}\": $@"); + return undef; + } + + # Any extras listed in "match_log" + if ($t and exists $t->{match_log}) { + for my $k (keys %{ $t->{match_log} || {} }) { + my($neg,$fn) = ($k =~ m/^(-?)(.*)$/); + next if (!$fn or exists $FILE{$fn}); + eval { + $FILE{$fn}{fn} = $fn; + $FILE{$fn}{fd} = new FileHandle($fn, O_RDWR|O_CREAT) or die "$!\n"; + $FILE{$fn}{fd}->blocking(0); + $FILE{$fn}{fd}->sysseek(0, 2); + $FILE{$fn}{buf} = ""; + }; + if ($@) { + msg("Warning: Failed to open file \"$fn\": $@"); + return undef; + } + } + } +} + +sub encode_chunked { + my($data, $size) = @_; + $size = 128 unless ($size); + my $chunked = ""; + + my $n = 0; + my $bytes = length($data); + while ($bytes >= $size) { + $chunked .= sprintf "%x\x0d\x0a%s\x0d\x0a", $size, substr($data, $n, $size); + $n += $size; + $bytes -= $size; + } + if ($bytes) { + $chunked .= sprintf "%x\x0d\x0a%s\x0d\x0a", $bytes, substr($data, $n, $bytes); + } + $chunked .= "0\x0d\x0a\x0d\x0a" +} diff --git a/2.5.13/2.5.x/apache2/t/run-unit-tests.pl.in b/2.5.13/2.5.x/apache2/t/run-unit-tests.pl.in new file mode 100755 index 00000000..66212c1c --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/run-unit-tests.pl.in @@ -0,0 +1,161 @@ +#!@PERL@ +# +# Run unit tests. +# +# Syntax: +# All: run-tests.pl +# All in file: run-tests.pl file +# Nth in file: run-tests.pl file N +# +use strict; +use POSIX qw(WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG); +use File::Basename qw(basename dirname); +use FileHandle; +use IPC::Open2 qw(open2); + +my @TYPES = qw(tfn op action); +my $TEST = "./msc_test"; +my $SCRIPT = basename($0); +my $SCRIPTDIR = dirname($0); +my $PASSED = 0; +my $TOTAL = 0; +my $DEBUG = $ENV{MSC_TEST_DEBUG} || 0; + +if (defined $ARGV[0]) { + runfile(dirname($ARGV[0]), basename($ARGV[0]), $ARGV[1]); + done(); +} + +for my $type (sort @TYPES) { + my $dir = "$SCRIPTDIR/$type"; + my @cfg = (); + + # Get test names + opendir(DIR, "$dir") or quit(1, "Failed to open \"$dir\": $!"); + @cfg = grep { /\.t$/ && -f "$dir/$_" } readdir(DIR); + closedir(DIR); + + for my $cfg (sort @cfg) { + runfile($dir, $cfg); + } + +} +done(); + + +sub runfile { + my($dir, $cfg, $testnum) = @_; + my $fn = "$dir/$cfg"; + my @data = (); + my $edata; + my @C = (); + my @test = (); + my $teststr; + my $n = 0; + my $pass = 0; + + open(CFG, "<$fn") or quit(1, "Failed to open \"$fn\": $!"); + @data = ; + + $edata = q/@C = (/ . join("", @data) . q/)/; + eval $edata; + quit(1, "Failed to read test data \"$cfg\": $@") if ($@); + + unless (@C) { + msg("\nNo tests defined for $fn"); + return; + } + + msg("\nLoaded ".@C." tests from $fn"); + for my $t (@C) { + $n++; + next if (defined $testnum and $n != $testnum); + + my %t = %{$t || {}}; + my $id = sprintf("%6d", $n); + my $in = (exists($t{input}) and defined($t{input})) ? $t{input} : ""; + my $out; + my $test_in = new FileHandle(); + my $test_out = new FileHandle(); + my $test_pid; + my $rc = 0; + my $param; + + if ($t{type} eq "tfn") { + $param = escape($t{output}); + } + elsif ($t{type} eq "op") { + $param = escape($t{param}); + } + elsif ($t{type} eq "action") { + $param = escape($t{param}); + } + else { + quit(1, "Unknown type \"$t{type}\" - should be one of: " . join(",",@TYPES)); + } + + @test = ("-t", $t{type}, "-n", $t{name}, "-p", $param, "-D", "$DEBUG", (exists($t{ret}) ? ("-r", $t{ret}) : ()), (exists($t{iterations}) ? ("-I", $t{iterations}) : ()), (exists($t{prerun}) ? ("-P", $t{prerun}) : ())); + $teststr = "$TEST " . join(" ", map { "\"$_\"" } @test); + $test_pid = open2($test_out, $test_in, $TEST, @test) or quit(1, "Failed to execute test: $teststr\": $!"); + print $test_in "$in"; + close $test_in; + $out = join("\\n", split(/\n/, <$test_out>)); + close $test_out; + waitpid($test_pid, 0); + + $rc = $?; + if ( WIFEXITED($rc) ) { + $rc = WEXITSTATUS($rc); + } + elsif( WIFSIGNALED($rc) ) { + msg("Test exited with signal " . WTERMSIG($rc) . "."); + msg("Executed: $teststr"); + $rc = -1; + } + else { + msg("Test exited with unknown error."); + $rc = -1; + } + + if ($rc == 0) { + $pass++; + } + + msg(sprintf("%s) %s \"%s\"%s: %s%s", $id, $t{type}, $t{name}, (exists($t{comment}) ? " $t{comment}" : ""), ($rc ? "failed" : "passed"), ((defined($out) && $out ne "")? " ($out)" : ""))); + + } + + $TOTAL += $testnum ? 1 : $n; + $PASSED += $pass; + + msg(sprintf("Passed: %2d; Failed: %2d", $pass, $testnum ? (1 - $pass) : ($n - $pass))); +} + +sub escape { + my @new = (); + for my $c (split(//, $_[0])) { + push @new, ((ord($c) >= 0x20 and ord($c) <= 0x7e) ? $c : sprintf("\\x%02x", ord($c))); + } + join('', @new); +} + +sub msg { + print STDOUT "@_\n" if (@_); +} + +sub quit { + my($ec,$msg) = @_; + $ec = 0 unless (defined $_[0]); + + msg("$msg") if (defined $msg); + + exit $ec; +} + +sub done { + if ($PASSED != $TOTAL) { + quit(1, "\n$PASSED/$TOTAL tests passed."); + } + + quit(0, "\nAll tests passed ($TOTAL)."); +} diff --git a/2.5.13/2.5.x/apache2/t/tfn/base64Decode.t b/2.5.13/2.5.x/apache2/t/tfn/base64Decode.t new file mode 100644 index 00000000..59d88029 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/base64Decode.t @@ -0,0 +1,51 @@ +### Empty +{ + type => "tfn", + name => "base64Decode", + input => "", + output => "", + ret => 0, +}, + +### Test values with varying lengths to check padding +{ + type => "tfn", + name => "base64Decode", + input => "VGVzdENhc2U=", + output => "TestCase", + ret => 1, +}, +{ + type => "tfn", + name => "base64Decode", + input => "VGVzdENhc2Ux", + output => "TestCase1", + ret => 1, +}, +{ + type => "tfn", + name => "base64Decode", + input => "VGVzdENhc2UxMg==", + output => "TestCase12", + ret => 1, +}, + + +### Check with a NUL +{ + type => "tfn", + name => "base64Decode", + input => "VGVzdABDYXNl", + output => "Test\0Case", + ret => 1, +}, + +### Invalid +# What should happen here? Probably just fail and leave alone. +{ + type => "tfn", + name => "base64Decode", + input => "VGVzdENhc2U=\0VGVzdENhc2U=", + output => "TestCase", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/base64Encode.t b/2.5.13/2.5.x/apache2/t/tfn/base64Encode.t new file mode 100644 index 00000000..a5a6f83b --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/base64Encode.t @@ -0,0 +1,40 @@ +### Empty +{ + type => "tfn", + name => "base64Encode", + input => "", + output => "", + ret => 0, +}, + +### Test values with varying lengths to check padding +{ + type => "tfn", + name => "base64Encode", + input => "TestCase", + output => "VGVzdENhc2U=", + ret => 1, +}, +{ + type => "tfn", + name => "base64Encode", + input => "TestCase1", + output => "VGVzdENhc2Ux", + ret => 1, +}, +{ + type => "tfn", + name => "base64Encode", + input => "TestCase12", + output => "VGVzdENhc2UxMg==", + ret => 1, +}, + +### Check with a NUL +{ + type => "tfn", + name => "base64Encode", + input => "Test\0Case", + output => "VGVzdABDYXNl", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/compressWhitespace.t b/2.5.13/2.5.x/apache2/t/tfn/compressWhitespace.t new file mode 100644 index 00000000..fe6179ed --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/compressWhitespace.t @@ -0,0 +1,50 @@ +### Empty +{ + type => "tfn", + name => "compressWhitespace", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "compressWhitespace", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "compressWhitespace", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, +{ + type => "tfn", + name => "compressWhitespace", + input => "Test Case", + output => "Test Case", + ret => 0, +}, + + +### Compress space/tab +{ + type => "tfn", + name => "compressWhitespace", + input => " Test \t Case ", + output => " Test Case ", + ret => 1, +}, + +### Pretty much everything in one +{ + type => "tfn", + name => "compressWhitespace", + input => "This is a test case with a tab \t, vtab \x0b, newline \x0a, return \x0d, formfeed \f, and a NUL\0 in it with a CRLF at the end.\x0d\x0a", + output => "This is a test case with a tab , vtab , newline , return , formfeed , and a NUL\0 in it with a CRLF at the end. ", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/cssDecode.t b/2.5.13/2.5.x/apache2/t/tfn/cssDecode.t new file mode 100644 index 00000000..180186bd --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/cssDecode.t @@ -0,0 +1,58 @@ +### Empty +{ + type => "tfn", + name => "cssDecode", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "cssDecode", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "cssDecode", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, + +### Valid Sequences +{ + type => "tfn", + name => "cssDecode", + input => "test\\a\\b\\f\\n\\r\\t\\v\\?\\'\\\"\\0\\12\\123\\1234\\12345\\123456\\ff01\\ff5e\\\n\\0 string", + output => qq(test\x0a\x0b\x0fnrtv?'"\x00\x12\x23\x34\x45\x56\x21\x7e\x00 string), + ret => 1, +}, + +### Invalid Sequences +# Trailing escape == line continuation with no line following (ie nothing) +{ + type => "tfn", + name => "cssDecode", + input => "test\\", + output => "test", + ret => 1, +}, + +# Edge cases +# "\1A" == "\x1A" +# "\1 A" == "\x01A" +# "\1234567" == "\x567" +# "\123456 7" == "\x567" +# "\1x" == "\x01x" +# "\1 x" == "\x01 x" +{ + type => "tfn", + name => "cssDecode", + input => "\\1A\\1 A\\1234567\\123456 7\\1x\\1 x", + output => "\x1A\x01A\x567\x567\x01x\x01x", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/escapeSeqDecode.t b/2.5.13/2.5.x/apache2/t/tfn/escapeSeqDecode.t new file mode 100644 index 00000000..7f362e9b --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/escapeSeqDecode.t @@ -0,0 +1,114 @@ +### Empty +{ + type => "tfn", + name => "escapeSeqDecode", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "escapeSeqDecode", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "escapeSeqDecode", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, + +### Valid Sequences +{ + type => "tfn", + name => "escapeSeqDecode", + input => "\\a\\b\\f\\n\\r\\t\\v\\?\\'\\\"\\0\\12\\123\\x00\\xff", + output => "\a\b\f\x0a\x0d\t\x0b?'\"\x00\x0a\x53\x00\xff", + ret => 1, +}, +{ + type => "tfn", + name => "escapeSeqDecode", + input => "\\a\\b\\f\\n\\r\\t\\v\0\\?\\'\\\"\\0\\12\\123\\x00\\xff", + output => "\a\b\f\x0a\x0d\t\x0b\0?'\"\x00\x0a\x53\x00\xff", + ret => 1, +}, + +### Invalid Sequences +# \8 and \9 are not octal +# \666 is a byte overflow (0x1b6) and should be truncated to a byte as 0xb6 +# \xag and \xga are not hex, +# \0123 is \012 + '3' +{ + type => "tfn", + name => "escapeSeqDecode", + input => "\\8\\9\\666\\xag\\xga\\0123", + output => "89\xb6xagxga\x0a3", + ret => 1, +}, + +# \x, \x0 lack enough hex digits +{ + type => "tfn", + name => "escapeSeqDecode", + input => "\\x", + output => "x", + ret => 1, +}, +{ + type => "tfn", + name => "escapeSeqDecode", + input => "\\x\\x0", + output => "xx0", + ret => 1, +}, +{ + type => "tfn", + name => "escapeSeqDecode", + input => "\\x\\x0\0", + output => "xx0\0", + ret => 1, +}, +# Octal at end +{ + type => "tfn", + name => "escapeSeqDecode", + input => "\\0", + output => "\x00", + ret => 1, +}, +{ + type => "tfn", + name => "escapeSeqDecode", + input => "\\01", + output => "\x01", + ret => 1, +}, +{ + type => "tfn", + name => "escapeSeqDecode", + input => "\\012", + output => "\x0a", + ret => 1, +}, +# A forward slash with nothing after +{ + type => "tfn", + name => "escapeSeqDecode", + input => "\\", + output => "\\", + ret => 0, +}, +# A forward slash with NUL after +{ + type => "tfn", + name => "escapeSeqDecode", + input => "\\\0", + output => "\0", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/hexDecode.t b/2.5.13/2.5.x/apache2/t/tfn/hexDecode.t new file mode 100644 index 00000000..ac6b4236 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/hexDecode.t @@ -0,0 +1,50 @@ +### Empty +{ + type => "tfn", + name => "hexDecode", + input => "", + output => "", + ret => 1, +}, + +### Basic +{ + type => "tfn", + name => "hexDecode", + input => "5465737443617365", + output => "TestCase", + ret => 1, +}, + +### Basic w/NULL +{ + type => "tfn", + name => "hexDecode", + input => "546573740043617365", + output => "Test\0Case", + ret => 1, +}, + +### Invalid +# What should happen here? Probably just fail and leave alone. +{ + type => "tfn", + name => "hexDecode", + input => "01234567890a0z01234567890a", + output => "\x01#Eg\x89\x0a#\x01#Eg\x89\x0a", + ret => 1, +}, +{ + type => "tfn", + name => "hexDecode", + input => "01234567890az", + output => "\x01#Eg\x89\x0a", + ret => 1, +}, +{ + type => "tfn", + name => "hexDecode", + input => "01234567890a0", + output => "\x01#Eg\x89\x0a", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/hexEncode.t b/2.5.13/2.5.x/apache2/t/tfn/hexEncode.t new file mode 100644 index 00000000..16dc43d6 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/hexEncode.t @@ -0,0 +1,26 @@ +### Empty +{ + type => "tfn", + name => "hexEncode", + input => "", + output => "", + ret => 1, +}, + +### Basic +{ + type => "tfn", + name => "hexEncode", + input => "TestCase", + output => "5465737443617365", + ret => 1, +}, + +### Basic w/NULL +{ + type => "tfn", + name => "hexEncode", + input => "Test\0Case", + output => "546573740043617365", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/htmlEntityDecode.t b/2.5.13/2.5.x/apache2/t/tfn/htmlEntityDecode.t new file mode 100644 index 00000000..201bd6be --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/htmlEntityDecode.t @@ -0,0 +1,58 @@ +### Empty +{ + type => "tfn", + name => "htmlEntityDecode", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "htmlEntityDecode", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "htmlEntityDecode", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, + +### Valid +# With ; +{ + type => "tfn", + name => "htmlEntityDecode", + input => "�� � \0d"&<> ", + output => "\0\0\x20\x20\0\x20\0\x64\"&<>\xa0", + ret => 1, +}, +# Without ; +{ + type => "tfn", + name => "htmlEntityDecode", + input => "�� � \0d"&<> ", + output => "\0\0\x20\x20\0\x20\0\x64\"&<>\xa0", + ret => 1, +}, + +### Invalid +{ + type => "tfn", + name => "htmlEntityDecode", + input => "&#xg;&#Xg;&#xg0;g;&#a;\0&#a2;a&#a00;a0; a;&foo;", + output => "&#xg;&#Xg;&#xg0;\x02g;&#a;\0&#a2;\x03a&#a00;\x01a0;\x0aa;&foo;", + ret => 1, +}, +{ + type => "tfn", + name => "htmlEntityDecode", + input => "&#xg&#Xg&#xg0g&#a\0&#a2a&#a00a0 a&foo", + output => "&#xg&#Xg&#xg0\x02g&#a\0&#a2\x03a&#a00\x01a0\x0aa&foo", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/jsDecode.t b/2.5.13/2.5.x/apache2/t/tfn/jsDecode.t new file mode 100644 index 00000000..9774b7ff --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/jsDecode.t @@ -0,0 +1,129 @@ +### Empty +{ + type => "tfn", + name => "jsDecode", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "jsDecode", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "jsDecode", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, + +### Valid Sequences +{ + type => "tfn", + name => "jsDecode", + input => "\\a\\b\\f\\n\\r\\t\\v\\?\\'\\\"\\0\\12\\123\\x00\\xff\\u0021\\uff01", + output => "\a\b\f\x0a\x0d\t\x0b?'\"\x00\x0a\x53\x00\xff\x21\x21", + ret => 1, +}, +{ + type => "tfn", + name => "jsDecode", + input => "\\a\\b\\f\\n\\r\\t\\v\0\\?\\'\\\"\\0\\12\\123\\x00\\xff\\u0021\\uff01", + output => "\a\b\f\x0a\x0d\t\x0b\0?'\"\x00\x0a\x53\x00\xff\x21\x21", + ret => 1, +}, + +### Invalid Sequences +# \8 and \9 are not octal +# \666 is \66 + '6' (JS does not allow the overflow as C does) +# \u00ag, \u00ga, \u0zaa, \uz0aa are not hex +# \xag and \xga are not hex, +# \0123 is \012 + '3' +{ + type => "tfn", + name => "jsDecode", + input => "\\8\\9\\666\\u00ag\\u00ga\\u0zaa\\uz0aa\\xag\\xga\\0123\\u00a", + output => "89\x366u00agu00gau0zaauz0aaxagxga\x0a3u00a", + ret => 1, +}, + +# \x, \x0 lack enough hex digits +{ + type => "tfn", + name => "jsDecode", + input => "\\x", + output => "x", + ret => 1, +}, +{ + type => "tfn", + name => "jsDecode", + input => "\\x\\x0", + output => "xx0", + ret => 1, +}, +{ + type => "tfn", + name => "jsDecode", + input => "\\x\\x0\0", + output => "xx0\0", + ret => 1, +}, +# \u, \u0 \u01, \u012 lack enough hex digits +{ + type => "tfn", + name => "jsDecode", + input => "\\u", + output => "u", + ret => 1, +}, +{ + type => "tfn", + name => "jsDecode", + input => "\\u\\u0", + output => "uu0", + ret => 1, +}, +{ + type => "tfn", + name => "jsDecode", + input => "\\u\\u0\\u01", + output => "uu0u01", + ret => 1, +}, +{ + type => "tfn", + name => "jsDecode", + input => "\\u\\u0\\u01\\u012", + output => "uu0u01u012", + ret => 1, +}, +{ + type => "tfn", + name => "jsDecode", + input => "\\u\\u0\\u01\\u012\0", + output => "uu0u01u012\0", + ret => 1, +}, +# A forward slash with nothing after +{ + type => "tfn", + name => "jsDecode", + input => "\\", + output => "\\", + ret => 0, +}, +# A forward slash with NUL after +{ + type => "tfn", + name => "jsDecode", + input => "\\\0", + output => "\0", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/length.t b/2.5.13/2.5.x/apache2/t/tfn/length.t new file mode 100644 index 00000000..53f16024 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/length.t @@ -0,0 +1,44 @@ +### Empty +{ + type => "tfn", + name => "length", + input => "", + output => "0", + ret => 1, +}, + + +### Basic normal and large +{ + type => "tfn", + name => "length", + input => "0123456789abcdef", + output => "16", + ret => 1, +}, +# ENH: This sometimes fails w/4096 length +#{ +# type => "tfn", +# name => "length", +# input => ('x' x 8192), +# output => "8192", +# ret => 1, +#}, + +### With TAB +{ + type => "tfn", + name => "length", + input => "0123456789\tabcdef", + output => "17", + ret => 1, +}, + +### With NUL +{ + type => "tfn", + name => "length", + input => "Test\0Case", + output => "9", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/lowercase.t b/2.5.13/2.5.x/apache2/t/tfn/lowercase.t new file mode 100644 index 00000000..bd55f4aa --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/lowercase.t @@ -0,0 +1,40 @@ +### Empty +{ + type => "tfn", + name => "lowercase", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "lowercase", + input => "testcase", + output => "testcase", + ret => 0, +}, +{ + type => "tfn", + name => "lowercase", + input => "test\0case", + output => "test\0case", + ret => 0, +}, + +### Basic +{ + type => "tfn", + name => "lowercase", + input => "TestCase", + output => "testcase", + ret => 1, +}, +{ + type => "tfn", + name => "lowercase", + input => "Test\0Case", + output => "test\0case", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/md5.t b/2.5.13/2.5.x/apache2/t/tfn/md5.t new file mode 100644 index 00000000..f9956d67 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/md5.t @@ -0,0 +1,26 @@ +### Empty +{ + type => "tfn", + name => "md5", + input => "", + output => "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\x09\x98\xec\xf8\x42\x7e", + ret => 1, +}, + +### Basic +{ + type => "tfn", + name => "md5", + input => "TestCase", + output => "\xc9\xab\xa2\xc3\xe6\x01\x26\x16\x9e\x80\xe9\xa2\x6b\xa2\x73\xc1", + ret => 1, +}, + +### Binary w/NUL +{ + type => "tfn", + name => "md5", + input => "\x00\x01\x02\x03\x04\x05\x06\x07\x08", + output => "\xa6\xe7\xd3\xb4\x6f\xdf\xaf\x0b\xde\x2a\x1f\x83\x2a\x00\xd2\xde", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/normalisePath.t b/2.5.13/2.5.x/apache2/t/tfn/normalisePath.t new file mode 100644 index 00000000..987aaaa1 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/normalisePath.t @@ -0,0 +1,224 @@ +### Empty +{ + type => "tfn", + name => "normalisePath", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "normalisePath", + input => "/foo/bar/baz", + output => "/foo/bar/baz", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePath", + input => "/foo/bar\0/baz", + output => "/foo/bar\0/baz", + ret => 0, +}, + +### Basic +{ + type => "tfn", + name => "normalisePath", + input => "x", + output => "x", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePath", + input => ".", + output => "", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "./", + output => "", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "./..", + output => "..", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "./../", + output => "../", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "..", + output => "..", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePath", + input => "../", + output => "../", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePath", + input => "../.", + output => "..", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => ".././", + output => "../", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "../..", + output => "../..", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePath", + input => "../../", + output => "../../", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePath", + input => "/dir/foo//bar", + output => "/dir/foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/foo//bar/", + output => "dir/foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/../foo", + output => "foo", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/../../foo", + output => "../foo", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./.././../../foo/bar", + output => "../../foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./.././../../foo/bar/.", + output => "../../foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./.././../../foo/bar/./", + output => "../../foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./.././../../foo/bar/..", + output => "../../foo", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./.././../../foo/bar/../", + output => "../../foo/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./.././../../foo/bar/", + output => "../../foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir//.//..//.//..//..//foo//bar", + output => "../../foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir//.//..//.//..//..//foo//bar//", + output => "../../foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/subdir/subsubdir/subsubsubdir/../../..", + output => "dir", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./subdir/./subsubdir/./subsubsubdir/../../..", + output => "dir", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./subdir/../subsubdir/../subsubsubdir/..", + output => "dir", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "/dir/./subdir/../subsubdir/../subsubsubdir/../", + output => "/dir/", + ret => 1, +}, + +### With NUL +{ + type => "tfn", + name => "normalisePath", + input => "/./.././../../../../../../../\0/../etc/./passwd", + output => "/etc/passwd", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/normalisePathWin.t b/2.5.13/2.5.x/apache2/t/tfn/normalisePathWin.t new file mode 100644 index 00000000..dc0f0c3e --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/normalisePathWin.t @@ -0,0 +1,224 @@ +### Empty +{ + type => "tfn", + name => "normalisePathWin", + input => "", + output => "", + ret => 0, +}, + +### Nothing but switch slashes +{ + type => "tfn", + name => "normalisePathWin", + input => "\\foo\\bar\\baz", + output => "/foo/bar/baz", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "\\foo\\bar\0\\baz", + output => "/foo/bar\0/baz", + ret => 1, +}, + +### Basics +{ + type => "tfn", + name => "normalisePathWin", + input => "x", + output => "x", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => ".", + output => "", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => ".\\", + output => "", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => ".\\..", + output => "..", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => ".\\..\\", + output => "../", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "..", + output => "..", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "..\\", + output => "../", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "..\\.", + output => "..", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "..\\.\\", + output => "../", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "..\\..", + output => "../..", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "..\\..\\", + output => "../../", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "\\dir\\foo\\\\bar", + output => "/dir/foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\foo\\\\bar\\", + output => "dir/foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\..\\foo", + output => "foo", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\..\\..\\foo", + output => "../foo", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\..\\.\\..\\..\\foo\\bar", + output => "../../foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\..\\.\\..\\..\\foo\\bar\\.", + output => "../../foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\..\\.\\..\\..\\foo\\bar\\.\\", + output => "../../foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\..\\.\\..\\..\\foo\\bar\\..", + output => "../../foo", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\..\\.\\..\\..\\foo\\bar\\..\\", + output => "../../foo/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\..\\.\\..\\..\\foo\\bar\\", + output => "../../foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\\\.\\\\..\\\\.\\\\..\\\\..\\\\foo\\\\bar", + output => "../../foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\\\.\\\\..\\\\.\\\\..\\\\..\\\\foo\\\\bar\\\\", + output => "../../foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\subdir\\subsubdir\\subsubsubdir\\..\\..\\..", + output => "dir", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\subdir\\.\\subsubdir\\.\\subsubsubdir\\..\\..\\..", + output => "dir", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\subdir\\..\\subsubdir\\..\\subsubsubdir\\..", + output => "dir", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "\\dir\\.\\subdir\\..\\subsubdir\\..\\subsubsubdir\\..\\", + output => "/dir/", + ret => 1, +}, + +### With NUL +{ + type => "tfn", + name => "normalisePathWin", + input => "\\.\\..\\.\\..\\..\\..\\..\\..\\..\\..\\\0\\..\\etc\\.\\passwd", + output => "/etc/passwd", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/parityEven7bit.t b/2.5.13/2.5.x/apache2/t/tfn/parityEven7bit.t new file mode 100644 index 00000000..3d02cc42 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/parityEven7bit.t @@ -0,0 +1,34 @@ +### Empty +{ + type => "tfn", + name => "parityEven7bit", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "parityEven7bit", + input => "cefijloqrtwx03569ABDGHKMNPSUVYZ", + output => "cefijloqrtwx03569ABDGHKMNPSUVYZ", + ret => 0, +}, + +### Parity +{ + type => "tfn", + name => "parityEven7bit", + input => "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", + output => "\xe1\xe2c\xe4ef\xe7\xe8ij\xebl\xed\xeeo\xf0qr\xf3t\xf5\xf6wx\xf9\xfa0\xb1\xb23\xb456\xb7\xb89AB\xc3D\xc5\xc6GH\xc9\xcaK\xccMN\xcfP\xd1\xd2S\xd4UV\xd7\xd8YZ", + ret => 1, +}, +{ + type => "tfn", + name => "parityEven7bit", + input => "abcdefghijklmnopqrstuvwxyz\x000123456789\x00ABCDEFGHIJKLMNOPQRSTUVWXYZ", + output => "\xe1\xe2c\xe4ef\xe7\xe8ij\xebl\xed\xeeo\xf0qr\xf3t\xf5\xf6wx\xf9\xfa\x000\xb1\xb23\xb456\xb7\xb89\x00AB\xc3D\xc5\xc6GH\xc9\xcaK\xccMN\xcfP\xd1\xd2S\xd4UV\xd7\xd8YZ", + ret => 1, +}, + diff --git a/2.5.13/2.5.x/apache2/t/tfn/parityOdd7bit.t b/2.5.13/2.5.x/apache2/t/tfn/parityOdd7bit.t new file mode 100644 index 00000000..61a7eacd --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/parityOdd7bit.t @@ -0,0 +1,34 @@ +### Empty +{ + type => "tfn", + name => "parityOdd7bit", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "parityOdd7bit", + input => "abdghkmnpsuvyz12478CEFIJLOQRTW", + output => "abdghkmnpsuvyz12478CEFIJLOQRTW", + ret => 0, +}, + +### Parity +{ + type => "tfn", + name => "parityOdd7bit", + input => "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", + output => "ab\xe3d\xe5\xe6gh\xe9\xeak\xecmn\xefp\xf1\xf2s\xf4uv\xf7\xf8yz\xb012\xb34\xb5\xb678\xb9\xc1\xc2C\xc4EF\xc7\xc8IJ\xcbL\xcd\xceO\xd0QR\xd3T\xd5\xd6WX\xd9\xda", + ret => 1, +}, +{ + type => "tfn", + name => "parityOdd7bit", + input => "abcdefghijklmnopqrstuvwxyz\x000123456789\x00ABCDEFGHIJKLMNOPQRSTUVWXYZ", + output => "ab\xe3d\xe5\xe6gh\xe9\xeak\xecmn\xefp\xf1\xf2s\xf4uv\xf7\xf8yz\x80\xb012\xb34\xb5\xb678\xb9\x80\xc1\xc2C\xc4EF\xc7\xc8IJ\xcbL\xcd\xceO\xd0QR\xd3T\xd5\xd6WX\xd9\xda", + ret => 1, +}, + diff --git a/2.5.13/2.5.x/apache2/t/tfn/parityZero7bit.t b/2.5.13/2.5.x/apache2/t/tfn/parityZero7bit.t new file mode 100644 index 00000000..41862b56 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/parityZero7bit.t @@ -0,0 +1,33 @@ +### Empty +{ + type => "tfn", + name => "parityZero7bit", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "parityZero7bit", + input => "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", + output => "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", + ret => 0, +}, +{ + type => "tfn", + name => "parityZero7bit", + input => "abcdefghijklmnopqrstuvwxyz\x000123456789\x00ABCDEFGHIJKLMNOPQRSTUVWXYZ", + output => "abcdefghijklmnopqrstuvwxyz\x000123456789\x00ABCDEFGHIJKLMNOPQRSTUVWXYZ", + ret => 0, +}, + +### Basic +{ + type => "tfn", + name => "parityZero7bit", + input => "\x80\x00\x8f\xff", + output => "\x00\x00\x0f\x7f", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/removeNulls.t b/2.5.13/2.5.x/apache2/t/tfn/removeNulls.t new file mode 100644 index 00000000..c538b93d --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/removeNulls.t @@ -0,0 +1,62 @@ +### Empty +{ + type => "tfn", + name => "removeNulls", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "removeNulls", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "removeNulls", + input => "Test\x01Case", + output => "Test\x01Case", + ret => 0, +}, + + +### Basics +{ + type => "tfn", + name => "removeNulls", + input => "\0TestCase", + output => "TestCase", + ret => 1, +}, +{ + type => "tfn", + name => "removeNulls", + input => "Test\0Case", + output => "TestCase", + ret => 1, +}, +{ + type => "tfn", + name => "removeNulls", + input => "Test\0\0Case", + output => "TestCase", + ret => 1, +}, +{ + type => "tfn", + name => "removeNulls", + input => "TestCase\0", + output => "TestCase", + ret => 1, +}, +{ + type => "tfn", + name => "removeNulls", + input => "\0Test\0Case\0", + output => "TestCase", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/removeWhitespace.t b/2.5.13/2.5.x/apache2/t/tfn/removeWhitespace.t new file mode 100644 index 00000000..69cecae0 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/removeWhitespace.t @@ -0,0 +1,43 @@ +### Empty +{ + type => "tfn", + name => "removeWhitespace", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "removeWhitespace", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "removeWhitespace", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, + + +### Remove space/tab +{ + type => "tfn", + name => "removeWhitespace", + input => " Test \t Case ", + output => "TestCase", + ret => 1, +}, + +### Pretty much everything in one +{ + type => "tfn", + name => "removeWhitespace", + input => "This is a test case with a tab \t, vtab \x0b, newline \x0a, return \x0d, formfeed \f, and a NUL\0 in it with a CRLF at the end.\x0d\x0a", + output => "Thisisatestcasewithatab,vtab,newline,return,formfeed,andaNUL\0initwithaCRLFattheend.", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/replaceComments.t b/2.5.13/2.5.x/apache2/t/tfn/replaceComments.t new file mode 100644 index 00000000..332436e4 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/replaceComments.t @@ -0,0 +1,155 @@ +### Empty +{ + type => "tfn", + name => "replaceComments", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "replaceComments", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "replaceComments", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, + + +### Basics +{ + type => "tfn", + name => "replaceComments", + input => "/* TestCase */", + output => " ", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "/*TestCase*/", + output => " ", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "/* TestCase*/", + output => " ", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "/*TestCase */", + output => " ", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "Before/* TestCase */After", + output => "Before After", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "Before /* TestCase */ After", + output => "Before After", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "/* Test\nCase */", + output => " ", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "/* Test\x0d\x0aCase */", + output => " ", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "/* Test\x0aCase */", + output => " ", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "/* Test\x0dCase */", + output => " ", + ret => 1, +}, + +### Broken Comments +{ + type => "tfn", + name => "replaceComments", + input => "Before/* Test\x0d\x0aCase ", + output => "Before ", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "Before /* Test\x0aCase ", + output => "Before ", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "Before/* Test\x0d\x0aCase ", + output => "Before ", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "Before /* Test\x0aCase ", + output => "Before ", + ret => 1, +}, +{ + type => "tfn", + name => "replaceComments", + input => "Test\x0d\x0aCase */After", + output => "Test\x0d\x0aCase */After", + ret => 0, +}, +{ + type => "tfn", + name => "replaceComments", + input => "Test\x0aCase */ After", + output => "Test\x0aCase */ After", + ret => 0, +}, +{ + type => "tfn", + name => "replaceComments", + input => "Test\x0d\x0aCase */After", + output => "Test\x0d\x0aCase */After", + ret => 0, +}, +{ + type => "tfn", + name => "replaceComments", + input => "Test\x0aCase */ After", + output => "Test\x0aCase */ After", + ret => 0, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/replaceNulls.t b/2.5.13/2.5.x/apache2/t/tfn/replaceNulls.t new file mode 100644 index 00000000..3be063c1 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/replaceNulls.t @@ -0,0 +1,55 @@ +### Empty +{ + type => "tfn", + name => "replaceNulls", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "replaceNulls", + input => "TestCase", + output => "TestCase", + ret => 0, +}, + + +### Basics +{ + type => "tfn", + name => "replaceNulls", + input => "\0TestCase", + output => " TestCase", + ret => 1, +}, +{ + type => "tfn", + name => "replaceNulls", + input => "Test\0Case", + output => "Test Case", + ret => 1, +}, +{ + type => "tfn", + name => "replaceNulls", + input => "Test\0\0Case", + output => "Test Case", + ret => 1, +}, +{ + type => "tfn", + name => "replaceNulls", + input => "TestCase\0", + output => "TestCase ", + ret => 1, +}, +{ + type => "tfn", + name => "replaceNulls", + input => "\0Test\0Case\0", + output => " Test Case ", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/sha1.t b/2.5.13/2.5.x/apache2/t/tfn/sha1.t new file mode 100644 index 00000000..84d38de3 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/sha1.t @@ -0,0 +1,26 @@ +### Empty +{ + type => "tfn", + name => "sha1", + input => "", + output => "\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09", + ret => 1, +}, + +### Basic +{ + type => "tfn", + name => "sha1", + input => "TestCase", + output => "\xa7\x0c\xe3\x83\x89\xe3\x18\xbd\x2b\xe1\x8a\x01\x11\xc6\xdc\x76\xbd\x2c\xd9\xed", + ret => 1, +}, + +### Binary w/NUL +{ + type => "tfn", + name => "sha1", + input => "\x00\x01\x02\x03\x04\x05\x06\x07\x08", + output => "\x63\xbf\x60\xc7\x10\x5a\x07\xa2\xb1\x25\xbb\xf8\x9e\x61\xab\xda\xbc\x69\x78\xc2", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/trim.t b/2.5.13/2.5.x/apache2/t/tfn/trim.t new file mode 100644 index 00000000..7ac3f7f6 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/trim.t @@ -0,0 +1,68 @@ +### Empty +{ + type => "tfn", + name => "trim", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "trim", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "trim", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, + +### Basics +{ + type => "tfn", + name => "trim", + input => " TestCase", + output => "TestCase", + ret => 1, +}, +{ + type => "tfn", + name => "trim", + input => "TestCase ", + output => "TestCase", + ret => 1, +}, +{ + type => "tfn", + name => "trim", + input => " TestCase ", + output => "TestCase", + ret => 1, +}, +{ + type => "tfn", + name => "trim", + input => " Test Case ", + output => "Test Case", + ret => 1, +}, +{ + type => "tfn", + name => "trim", + input => " Test \0 Case ", + output => "Test \0 Case", + ret => 1, +}, +{ + type => "tfn", + name => "trim", + input => " Test \0 Case \r\n ", + output => "Test \0 Case", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/trimLeft.t b/2.5.13/2.5.x/apache2/t/tfn/trimLeft.t new file mode 100644 index 00000000..e2c2951a --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/trimLeft.t @@ -0,0 +1,69 @@ +### Empty +{ + type => "tfn", + name => "trimLeft", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "trimLeft", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "trimLeft", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, +{ + type => "tfn", + name => "trimLeft", + input => "TestCase ", + output => "TestCase ", + ret => 0, +}, + + +### Basics +{ + type => "tfn", + name => "trimLeft", + input => " TestCase", + output => "TestCase", + ret => 1, +}, +{ + type => "tfn", + name => "trimLeft", + input => " TestCase ", + output => "TestCase ", + ret => 1, +}, +{ + type => "tfn", + name => "trimLeft", + input => " Test Case ", + output => "Test Case ", + ret => 1, +}, +{ + type => "tfn", + name => "trimLeft", + input => " Test \0 Case ", + output => "Test \0 Case ", + ret => 1, +}, +{ + type => "tfn", + name => "trimLeft", + input => " Test \0 Case \r\n ", + output => "Test \0 Case \r\n ", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/trimRight.t b/2.5.13/2.5.x/apache2/t/tfn/trimRight.t new file mode 100644 index 00000000..f356333d --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/trimRight.t @@ -0,0 +1,68 @@ +### Empty +{ + type => "tfn", + name => "trimRight", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "trimRight", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "trimRight", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, +{ + type => "tfn", + name => "trimRight", + input => " TestCase", + output => " TestCase", + ret => 0, +}, + +### Basics +{ + type => "tfn", + name => "trimRight", + input => "TestCase ", + output => "TestCase", + ret => 1, +}, +{ + type => "tfn", + name => "trimRight", + input => " TestCase ", + output => " TestCase", + ret => 1, +}, +{ + type => "tfn", + name => "trimRight", + input => " Test Case ", + output => " Test Case", + ret => 1, +}, +{ + type => "tfn", + name => "trimRight", + input => " Test \0 Case ", + output => " Test \0 Case", + ret => 1, +}, +{ + type => "tfn", + name => "trimRight", + input => " Test \0 Case \r\n ", + output => " Test \0 Case", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/urlDecode.t b/2.5.13/2.5.x/apache2/t/tfn/urlDecode.t new file mode 100644 index 00000000..b0ea0072 --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/urlDecode.t @@ -0,0 +1,143 @@ +### Empty +{ + type => "tfn", + name => "urlDecode", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "urlDecode", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecode", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, + +### Valid +{ + type => "tfn", + name => "urlDecode", + input => "+%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f%20%21%22%23%24%25%26%27%28%29%2a%2b%2c%2d%2e%2f%30%31%32%33%34%35%36%37%38%39%3a%3b%3c%3d%3e%3f%40%41%42%43%44%45%46%47%48%49%4a%4b%4c%4d%4e%4f%50%51%52%53%54%55%56%57%58%59%5a%5b%5c%5d%5e%5f%60%61%62%63%64%65%66%67%68%69%6a%6b%6c%6d%6e%6f%70%71%72%73%74%75%76%77%78%79%7a%7b%7c%7d%7e%7f%80%81%82%83%84%85%86%87%88%89%8a%8b%8c%8d%8e%8f%90%91%92%93%94%95%96%97%98%99%9a%9b%9c%9d%9e%9f%a0%a1%a2%a3%a4%a5%a6%a7%a8%a9%aa%ab%ac%ad%ae%af%b0%b1%b2%b3%b4%b5%b6%b7%b8%b9%ba%bb%bc%bd%be%bf%c0%c1%c2%c3%c4%c5%c6%c7%c8%c9%ca%cb%cc%cd%ce%cf%d0%d1%d2%d3%d4%d5%d6%d7%d8%d9%da%db%dc%dd%de%df%e0%e1%e2%e3%e4%e5%e6%e7%e8%e9%ea%eb%ec%ed%ee%ef%f0%f1%f2%f3%f4%f5%f6%f7%f8%f9%fa%fb%fc%fd%fe%ff", + output => " \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecode", + input => "Test+Case", + output => "Test Case", + ret => 1, +}, + + +### Partial Invalid +{ + type => "tfn", + name => "urlDecode", + input => "%+", + output => "% ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecode", + input => "%%20", + output => "% ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecode", + input => "%0g%20", + output => "%0g ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecode", + input => "%0%20", + output => "%0 ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecode", + input => "%g0%20", + output => "%g0 ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecode", + input => "%g%20", + output => "%g ", + ret => 1, +}, + +### Invalid +{ + type => "tfn", + name => "urlDecode", + input => "%0%1%2%3%4%5%6%7%8%9%0%a%b%c%d%e%f", + output => "%0%1%2%3%4%5%6%7%8%9%0%a%b%c%d%e%f", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecode", + input => "%g0%g1%g2%g3%g4%g5%g6%g7%g8%g9%g0%ga%gb%gc%gd%ge%gf", + output => "%g0%g1%g2%g3%g4%g5%g6%g7%g8%g9%g0%ga%gb%gc%gd%ge%gf", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecode", + input => "%0g%1g%2g%3g%4g%5g%6g%7g%8g%9g%0g%ag%bg%cg%dg%eg%fg", + output => "%0g%1g%2g%3g%4g%5g%6g%7g%8g%9g%0g%ag%bg%cg%dg%eg%fg", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecode", + input => "%", + output => "%", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecode", + input => "%0", + output => "%0", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecode", + input => "%%", + output => "%%", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecode", + input => "%0g", + output => "%0g", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecode", + input => "%gg", + output => "%gg", + ret => 0, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/urlDecodeUni.t b/2.5.13/2.5.x/apache2/t/tfn/urlDecodeUni.t new file mode 100644 index 00000000..798542ca --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/urlDecodeUni.t @@ -0,0 +1,235 @@ +### Empty +{ + type => "tfn", + name => "urlDecodeUni", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "urlDecodeUni", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, + +### Valid +{ + type => "tfn", + name => "urlDecodeUni", + input => "+%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f%20%21%22%23%24%25%26%27%28%29%2a%2b%2c%2d%2e%2f%30%31%32%33%34%35%36%37%38%39%3a%3b%3c%3d%3e%3f%40%41%42%43%44%45%46%47%48%49%4a%4b%4c%4d%4e%4f%50%51%52%53%54%55%56%57%58%59%5a%5b%5c%5d%5e%5f%60%61%62%63%64%65%66%67%68%69%6a%6b%6c%6d%6e%6f%70%71%72%73%74%75%76%77%78%79%7a%7b%7c%7d%7e%7f%80%81%82%83%84%85%86%87%88%89%8a%8b%8c%8d%8e%8f%90%91%92%93%94%95%96%97%98%99%9a%9b%9c%9d%9e%9f%a0%a1%a2%a3%a4%a5%a6%a7%a8%a9%aa%ab%ac%ad%ae%af%b0%b1%b2%b3%b4%b5%b6%b7%b8%b9%ba%bb%bc%bd%be%bf%c0%c1%c2%c3%c4%c5%c6%c7%c8%c9%ca%cb%cc%cd%ce%cf%d0%d1%d2%d3%d4%d5%d6%d7%d8%d9%da%db%dc%dd%de%df%e0%e1%e2%e3%e4%e5%e6%e7%e8%e9%ea%eb%ec%ed%ee%ef%f0%f1%f2%f3%f4%f5%f6%f7%f8%f9%fa%fb%fc%fd%fe%ff", + output => " \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "Test+Case", + output => "Test Case", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "+%u0000%u0001%u0002%u0003%u0004%u0005%u0006%u0007%u0008%u0009%u000a%u000b%u000c%u000d%u000e%u000f%u0010%u0011%u0012%u0013%u0014%u0015%u0016%u0017%u0018%u0019%u001a%u001b%u001c%u001d%u001e%u001f%u0020%u0021%u0022%u0023%u0024%u0025%u0026%u0027%u0028%u0029%u002a%u002b%u002c%u002d%u002e%u002f%u0030%u0031%u0032%u0033%u0034%u0035%u0036%u0037%u0038%u0039%u003a%u003b%u003c%u003d%u003e%u003f%u0040%u0041%u0042%u0043%u0044%u0045%u0046%u0047%u0048%u0049%u004a%u004b%u004c%u004d%u004e%u004f%u0050%u0051%u0052%u0053%u0054%u0055%u0056%u0057%u0058%u0059%u005a%u005b%u005c%u005d%u005e%u005f%u0060%u0061%u0062%u0063%u0064%u0065%u0066%u0067%u0068%u0069%u006a%u006b%u006c%u006d%u006e%u006f%u0070%u0071%u0072%u0073%u0074%u0075%u0076%u0077%u0078%u0079%u007a%u007b%u007c%u007d%u007e%u007f%u0080%u0081%u0082%u0083%u0084%u0085%u0086%u0087%u0088%u0089%u008a%u008b%u008c%u008d%u008e%u008f%u0090%u0091%u0092%u0093%u0094%u0095%u0096%u0097%u0098%u0099%u009a%u009b%u009c%u009d%u009e%u009f%u00a0%u00a1%u00a2%u00a3%u00a4%u00a5%u00a6%u00a7%u00a8%u00a9%u00aa%u00ab%u00ac%u00ad%u00ae%u00af%u00b0%u00b1%u00b2%u00b3%u00b4%u00b5%u00b6%u00b7%u00b8%u00b9%u00ba%u00bb%u00bc%u00bd%u00be%u00bf%u00c0%u00c1%u00c2%u00c3%u00c4%u00c5%u00c6%u00c7%u00c8%u00c9%u00ca%u00cb%u00cc%u00cd%u00ce%u00cf%u00d0%u00d1%u00d2%u00d3%u00d4%u00d5%u00d6%u00d7%u00d8%u00d9%u00da%u00db%u00dc%u00dd%u00de%u00df%u00e0%u00e1%u00e2%u00e3%u00e4%u00e5%u00e6%u00e7%u00e8%u00e9%u00ea%u00eb%u00ec%u00ed%u00ee%u00ef%u00f0%u00f1%u00f2%u00f3%u00f4%u00f5%u00f6%u00f7%u00f8%u00f9%u00fa%u00fb%u00fc%u00fd%u00fe%u00ff", + output => " \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "+%u1100%u1101%u1102%u1103%u1104%u1105%u1106%u1107%u1108%u1109%u110a%u110b%u110c%u110d%u110e%u110f%u1110%u1111%u1112%u1113%u1114%u1115%u1116%u1117%u1118%u1119%u111a%u111b%u111c%u111d%u111e%u111f%u1120%u1121%u1122%u1123%u1124%u1125%u1126%u1127%u1128%u1129%u112a%u112b%u112c%u112d%u112e%u112f%u1130%u1131%u1132%u1133%u1134%u1135%u1136%u1137%u1138%u1139%u113a%u113b%u113c%u113d%u113e%u113f%u1140%u1141%u1142%u1143%u1144%u1145%u1146%u1147%u1148%u1149%u114a%u114b%u114c%u114d%u114e%u114f%u1150%u1151%u1152%u1153%u1154%u1155%u1156%u1157%u1158%u1159%u115a%u115b%u115c%u115d%u115e%u115f%u1160%u1161%u1162%u1163%u1164%u1165%u1166%u1167%u1168%u1169%u116a%u116b%u116c%u116d%u116e%u116f%u1170%u1171%u1172%u1173%u1174%u1175%u1176%u1177%u1178%u1179%u117a%u117b%u117c%u117d%u117e%u117f%u1180%u1181%u1182%u1183%u1184%u1185%u1186%u1187%u1188%u1189%u118a%u118b%u118c%u118d%u118e%u118f%u1190%u1191%u1192%u1193%u1194%u1195%u1196%u1197%u1198%u1199%u119a%u119b%u119c%u119d%u119e%u119f%u11a0%u11a1%u11a2%u11a3%u11a4%u11a5%u11a6%u11a7%u11a8%u11a9%u11aa%u11ab%u11ac%u11ad%u11ae%u11af%u11b0%u11b1%u11b2%u11b3%u11b4%u11b5%u11b6%u11b7%u11b8%u11b9%u11ba%u11bb%u11bc%u11bd%u11be%u11bf%u11c0%u11c1%u11c2%u11c3%u11c4%u11c5%u11c6%u11c7%u11c8%u11c9%u11ca%u11cb%u11cc%u11cd%u11ce%u11cf%u11d0%u11d1%u11d2%u11d3%u11d4%u11d5%u11d6%u11d7%u11d8%u11d9%u11da%u11db%u11dc%u11dd%u11de%u11df%u11e0%u11e1%u11e2%u11e3%u11e4%u11e5%u11e6%u11e7%u11e8%u11e9%u11ea%u11eb%u11ec%u11ed%u11ee%u11ef%u11f0%u11f1%u11f2%u11f3%u11f4%u11f5%u11f6%u11f7%u11f8%u11f9%u11fa%u11fb%u11fc%u11fd%u11fe%u11ff", + output => " \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + ret => 1, +}, +# Full Width ASCII +{ + type => "tfn", + name => "urlDecodeUni", + input => "%uff01%uff02%uff03%uff04%uff05%uff06%uff07%uff08%uff09%uff0a%uff0b%uff0c%uff0d%uff0e%uff0f%uff10%uff11%uff12%uff13%uff14%uff15%uff16%uff17%uff18%uff19%uff1a%uff1b%uff1c%uff1d%uff1e%uff1f%uff20%uff21%uff22%uff23%uff24%uff25%uff26%uff27%uff28%uff29%uff2a%uff2b%uff2c%uff2d%uff2e%uff2f%uff30%uff31%uff32%uff33%uff34%uff35%uff36%uff37%uff38%uff39%uff3a%uff3b%uff3c%uff3d%uff3e%uff3f%uff40%uff41%uff42%uff43%uff44%uff45%uff46%uff47%uff48%uff49%uff4a%uff4b%uff4c%uff4d%uff4e%uff4f%uff50%uff51%uff52%uff53%uff54%uff55%uff56%uff57%uff58%uff59%uff5a%uff5b%uff5c%uff5d%uff5e", + output => "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e", + ret => 1, +}, +# Invalid Full Width ASCII +{ + type => "tfn", + name => "urlDecodeUni", + input => "%uff00%uff7f%uff80%uff81%uff82%uff83%uff84%uff85%uff86%uff87%uff88%uff89%uff8a%uff8b%uff8c%uff8d%uff8e%uff8f%uff90%uff91%uff92%uff93%uff94%uff95%uff96%uff97%uff98%uff99%uff9a%uff9b%uff9c%uff9d%uff9e%uff9f%uffa0%uffa1%uffa2%uffa3%uffa4%uffa5%uffa6%uffa7%uffa8%uffa9%uffaa%uffab%uffac%uffad%uffae%uffaf%uffb0%uffb1%uffb2%uffb3%uffb4%uffb5%uffb6%uffb7%uffb8%uffb9%uffba%uffbb%uffbc%uffbd%uffbe%uffbf%uffc0%uffc1%uffc2%uffc3%uffc4%uffc5%uffc6%uffc7%uffc8%uffc9%uffca%uffcb%uffcc%uffcd%uffce%uffcf%uffd0%uffd1%uffd2%uffd3%uffd4%uffd5%uffd6%uffd7%uffd8%uffd9%uffda%uffdb%uffdc%uffdd%uffde%uffdf%uffe0%uffe1%uffe2%uffe3%uffe4%uffe5%uffe6%uffe7%uffe8%uffe9%uffea%uffeb%uffec%uffed%uffee%uffef%ufff0%ufff1%ufff2%ufff3%ufff4%ufff5%ufff6%ufff7%ufff8%ufff9%ufffa%ufffb%ufffc%ufffd%ufffe%uffff", + output => "\x00\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + ret => 1, +}, + +### Partial Invalid +{ + type => "tfn", + name => "urlDecodeUni", + input => "%+", + output => "% ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%%20", + output => "% ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%0g%20", + output => "%0g ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%0%20", + output => "%0 ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%g0%20", + output => "%g0 ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%g%20", + output => "%g ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%%u0020", + output => "% ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%0g%u0020", + output => "%0g ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%0%u0020", + output => "%0 ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%g0%u0020", + output => "%g0 ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%u%u0020", + output => "%u ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%u0%u0020", + output => "%u0 ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%u00%u0020", + output => "%u00 ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%u000%u0020", + output => "%u000 ", + ret => 1, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%u000g%u0020", + output => "%u000g ", + ret => 1, +}, + +### Invalid +{ + type => "tfn", + name => "urlDecodeUni", + input => "%0%1%2%3%4%5%6%7%8%9%0%a%b%c%d%e%f", + output => "%0%1%2%3%4%5%6%7%8%9%0%a%b%c%d%e%f", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%g0%g1%g2%g3%g4%g5%g6%g7%g8%g9%g0%ga%gb%gc%gd%ge%gf", + output => "%g0%g1%g2%g3%g4%g5%g6%g7%g8%g9%g0%ga%gb%gc%gd%ge%gf", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%0g%1g%2g%3g%4g%5g%6g%7g%8g%9g%0g%ag%bg%cg%dg%eg%fg", + output => "%0g%1g%2g%3g%4g%5g%6g%7g%8g%9g%0g%ag%bg%cg%dg%eg%fg", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%", + output => "%", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%0", + output => "%0", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%%", + output => "%%", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%0g", + output => "%0g", + ret => 0, +}, +{ + type => "tfn", + name => "urlDecodeUni", + input => "%gg", + output => "%gg", + ret => 0, +}, diff --git a/2.5.13/2.5.x/apache2/t/tfn/urlEncode.t b/2.5.13/2.5.x/apache2/t/tfn/urlEncode.t new file mode 100644 index 00000000..10f9bcab --- /dev/null +++ b/2.5.13/2.5.x/apache2/t/tfn/urlEncode.t @@ -0,0 +1,129 @@ +### Empty +{ + type => "tfn", + name => "urlEncode", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "urlEncode", + input => "TestCase", + output => "TestCase", + ret => 0, +}, + +### Valid +{ + type => "tfn", + name => "urlEncode", + input => " \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + output => "+%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f+%21%22%23%24%25%26%27%28%29*%2b%2c%2d%2e%2f0123456789%3a%3b%3c%3d%3e%3f%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5b%5c%5d%5e%5f%60abcdefghijklmnopqrstuvwxyz%7b%7c%7d%7e%7f%80%81%82%83%84%85%86%87%88%89%8a%8b%8c%8d%8e%8f%90%91%92%93%94%95%96%97%98%99%9a%9b%9c%9d%9e%9f%a0%a1%a2%a3%a4%a5%a6%a7%a8%a9%aa%ab%ac%ad%ae%af%b0%b1%b2%b3%b4%b5%b6%b7%b8%b9%ba%bb%bc%bd%be%bf%c0%c1%c2%c3%c4%c5%c6%c7%c8%c9%ca%cb%cc%cd%ce%cf%d0%d1%d2%d3%d4%d5%d6%d7%d8%d9%da%db%dc%dd%de%df%e0%e1%e2%e3%e4%e5%e6%e7%e8%e9%ea%eb%ec%ed%ee%ef%f0%f1%f2%f3%f4%f5%f6%f7%f8%f9%fa%fb%fc%fd%fe%ff", + ret => 1, +}, +{ + type => "tfn", + name => "urlEncode", + input => "Test Case", + output => "Test+Case", + ret => 1, +}, + + +### Partial Invalid +{ + type => "tfn", + name => "urlEncode", + input => "% ", + output => "%25+", + ret => 1, +}, +{ + type => "tfn", + name => "urlEncode", + input => "%0g ", + output => "%250g+", + ret => 1, +}, +{ + type => "tfn", + name => "urlEncode", + input => "%0 ", + output => "%250+", + ret => 1, +}, +{ + type => "tfn", + name => "urlEncode", + input => "%g0 ", + output => "%25g0+", + ret => 1, +}, +{ + type => "tfn", + name => "urlEncode", + input => "%g ", + output => "%25g+", + ret => 1, +}, + +### Invalid +{ + type => "tfn", + name => "urlEncode", + input => "%0%1%2%3%4%5%6%7%8%9%0%a%b%c%d%e%f", + output => "%250%251%252%253%254%255%256%257%258%259%250%25a%25b%25c%25d%25e%25f", + ret => 1, +}, +{ + type => "tfn", + name => "urlEncode", + input => "%g0%g1%g2%g3%g4%g5%g6%g7%g8%g9%g0%ga%gb%gc%gd%ge%gf", + output => "%25g0%25g1%25g2%25g3%25g4%25g5%25g6%25g7%25g8%25g9%25g0%25ga%25gb%25gc%25gd%25ge%25gf", + ret => 1, +}, +{ + type => "tfn", + name => "urlEncode", + input => "%0g%1g%2g%3g%4g%5g%6g%7g%8g%9g%0g%ag%bg%cg%dg%eg%fg", + output => "%250g%251g%252g%253g%254g%255g%256g%257g%258g%259g%250g%25ag%25bg%25cg%25dg%25eg%25fg", + ret => 1, +}, +{ + type => "tfn", + name => "urlEncode", + input => "%", + output => "%25", + ret => 1, +}, +{ + type => "tfn", + name => "urlEncode", + input => "%0", + output => "%250", + ret => 1, +}, +{ + type => "tfn", + name => "urlEncode", + input => "%%", + output => "%25%25", + ret => 1, +}, +{ + type => "tfn", + name => "urlEncode", + input => "%0g", + output => "%250g", + ret => 1, +}, +{ + type => "tfn", + name => "urlEncode", + input => "%gg", + output => "%25gg", + ret => 1, +}, diff --git a/2.5.13/2.5.x/apache2/utf8tables.h b/2.5.13/2.5.x/apache2/utf8tables.h new file mode 100644 index 00000000..d88809d8 --- /dev/null +++ b/2.5.13/2.5.x/apache2/utf8tables.h @@ -0,0 +1,818 @@ +/* + * 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 UTF8TABLES_H_ +#define UTF8TABLES_H_ + +/** + * This include file is used by acmp.c only; it's not included anywhere else. + */ + +typedef long acmp_utf8_char_t; + +static const char utf8_seq_lengths[256] = { + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4, 5,5,5,5,6,6,6,6, +}; + +static const acmp_utf8_char_t utf8_offsets[6] = { + 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL +}; + +/** + * How many element pairs are there in utf8_lcase_map + */ +#define UTF8_LCASEMAP_LEN 759 + +/** + * Table mapping is from PHP's mbstring extension, maps uppercase + */ +static const acmp_utf8_char_t utf8_lcase_map[UTF8_LCASEMAP_LEN * 2] = { + 0x00000061, 0x00000041, + 0x00000062, 0x00000042, + 0x00000063, 0x00000043, + 0x00000064, 0x00000044, + 0x00000065, 0x00000045, + 0x00000066, 0x00000046, + 0x00000067, 0x00000047, + 0x00000068, 0x00000048, + 0x00000069, 0x00000049, + 0x0000006a, 0x0000004a, + 0x0000006b, 0x0000004b, + 0x0000006c, 0x0000004c, + 0x0000006d, 0x0000004d, + 0x0000006e, 0x0000004e, + 0x0000006f, 0x0000004f, + 0x00000070, 0x00000050, + 0x00000071, 0x00000051, + 0x00000072, 0x00000052, + 0x00000073, 0x00000053, + 0x00000074, 0x00000054, + 0x00000075, 0x00000055, + 0x00000076, 0x00000056, + 0x00000077, 0x00000057, + 0x00000078, 0x00000058, + 0x00000079, 0x00000059, + 0x0000007a, 0x0000005a, + 0x000000b5, 0x0000039c, + 0x000000e0, 0x000000c0, + 0x000000e1, 0x000000c1, + 0x000000e2, 0x000000c2, + 0x000000e3, 0x000000c3, + 0x000000e4, 0x000000c4, + 0x000000e5, 0x000000c5, + 0x000000e6, 0x000000c6, + 0x000000e7, 0x000000c7, + 0x000000e8, 0x000000c8, + 0x000000e9, 0x000000c9, + 0x000000ea, 0x000000ca, + 0x000000eb, 0x000000cb, + 0x000000ec, 0x000000cc, + 0x000000ed, 0x000000cd, + 0x000000ee, 0x000000ce, + 0x000000ef, 0x000000cf, + 0x000000f0, 0x000000d0, + 0x000000f1, 0x000000d1, + 0x000000f2, 0x000000d2, + 0x000000f3, 0x000000d3, + 0x000000f4, 0x000000d4, + 0x000000f5, 0x000000d5, + 0x000000f6, 0x000000d6, + 0x000000f8, 0x000000d8, + 0x000000f9, 0x000000d9, + 0x000000fa, 0x000000da, + 0x000000fb, 0x000000db, + 0x000000fc, 0x000000dc, + 0x000000fd, 0x000000dd, + 0x000000fe, 0x000000de, + 0x000000ff, 0x00000178, + 0x00000101, 0x00000100, + 0x00000103, 0x00000102, + 0x00000105, 0x00000104, + 0x00000107, 0x00000106, + 0x00000109, 0x00000108, + 0x0000010b, 0x0000010a, + 0x0000010d, 0x0000010c, + 0x0000010f, 0x0000010e, + 0x00000111, 0x00000110, + 0x00000113, 0x00000112, + 0x00000115, 0x00000114, + 0x00000117, 0x00000116, + 0x00000119, 0x00000118, + 0x0000011b, 0x0000011a, + 0x0000011d, 0x0000011c, + 0x0000011f, 0x0000011e, + 0x00000121, 0x00000120, + 0x00000123, 0x00000122, + 0x00000125, 0x00000124, + 0x00000127, 0x00000126, + 0x00000129, 0x00000128, + 0x0000012b, 0x0000012a, + 0x0000012d, 0x0000012c, + 0x0000012f, 0x0000012e, + 0x00000131, 0x00000049, + 0x00000133, 0x00000132, + 0x00000135, 0x00000134, + 0x00000137, 0x00000136, + 0x0000013a, 0x00000139, + 0x0000013c, 0x0000013b, + 0x0000013e, 0x0000013d, + 0x00000140, 0x0000013f, + 0x00000142, 0x00000141, + 0x00000144, 0x00000143, + 0x00000146, 0x00000145, + 0x00000148, 0x00000147, + 0x0000014b, 0x0000014a, + 0x0000014d, 0x0000014c, + 0x0000014f, 0x0000014e, + 0x00000151, 0x00000150, + 0x00000153, 0x00000152, + 0x00000155, 0x00000154, + 0x00000157, 0x00000156, + 0x00000159, 0x00000158, + 0x0000015b, 0x0000015a, + 0x0000015d, 0x0000015c, + 0x0000015f, 0x0000015e, + 0x00000161, 0x00000160, + 0x00000163, 0x00000162, + 0x00000165, 0x00000164, + 0x00000167, 0x00000166, + 0x00000169, 0x00000168, + 0x0000016b, 0x0000016a, + 0x0000016d, 0x0000016c, + 0x0000016f, 0x0000016e, + 0x00000171, 0x00000170, + 0x00000173, 0x00000172, + 0x00000175, 0x00000174, + 0x00000177, 0x00000176, + 0x0000017a, 0x00000179, + 0x0000017c, 0x0000017b, + 0x0000017e, 0x0000017d, + 0x0000017f, 0x00000053, + 0x00000183, 0x00000182, + 0x00000185, 0x00000184, + 0x00000188, 0x00000187, + 0x0000018c, 0x0000018b, + 0x00000192, 0x00000191, + 0x00000195, 0x000001f6, + 0x00000199, 0x00000198, + 0x0000019e, 0x00000220, + 0x000001a1, 0x000001a0, + 0x000001a3, 0x000001a2, + 0x000001a5, 0x000001a4, + 0x000001a8, 0x000001a7, + 0x000001ad, 0x000001ac, + 0x000001b0, 0x000001af, + 0x000001b4, 0x000001b3, + 0x000001b6, 0x000001b5, + 0x000001b9, 0x000001b8, + 0x000001bd, 0x000001bc, + 0x000001bf, 0x000001f7, + 0x000001c6, 0x000001c4, + 0x000001c9, 0x000001c7, + 0x000001cc, 0x000001ca, + 0x000001ce, 0x000001cd, + 0x000001d0, 0x000001cf, + 0x000001d2, 0x000001d1, + 0x000001d4, 0x000001d3, + 0x000001d6, 0x000001d5, + 0x000001d8, 0x000001d7, + 0x000001da, 0x000001d9, + 0x000001dc, 0x000001db, + 0x000001dd, 0x0000018e, + 0x000001df, 0x000001de, + 0x000001e1, 0x000001e0, + 0x000001e3, 0x000001e2, + 0x000001e5, 0x000001e4, + 0x000001e7, 0x000001e6, + 0x000001e9, 0x000001e8, + 0x000001eb, 0x000001ea, + 0x000001ed, 0x000001ec, + 0x000001ef, 0x000001ee, + 0x000001f3, 0x000001f1, + 0x000001f5, 0x000001f4, + 0x000001f9, 0x000001f8, + 0x000001fb, 0x000001fa, + 0x000001fd, 0x000001fc, + 0x000001ff, 0x000001fe, + 0x00000201, 0x00000200, + 0x00000203, 0x00000202, + 0x00000205, 0x00000204, + 0x00000207, 0x00000206, + 0x00000209, 0x00000208, + 0x0000020b, 0x0000020a, + 0x0000020d, 0x0000020c, + 0x0000020f, 0x0000020e, + 0x00000211, 0x00000210, + 0x00000213, 0x00000212, + 0x00000215, 0x00000214, + 0x00000217, 0x00000216, + 0x00000219, 0x00000218, + 0x0000021b, 0x0000021a, + 0x0000021d, 0x0000021c, + 0x0000021f, 0x0000021e, + 0x00000223, 0x00000222, + 0x00000225, 0x00000224, + 0x00000227, 0x00000226, + 0x00000229, 0x00000228, + 0x0000022b, 0x0000022a, + 0x0000022d, 0x0000022c, + 0x0000022f, 0x0000022e, + 0x00000231, 0x00000230, + 0x00000233, 0x00000232, + 0x00000253, 0x00000181, + 0x00000254, 0x00000186, + 0x00000256, 0x00000189, + 0x00000257, 0x0000018a, + 0x00000259, 0x0000018f, + 0x0000025b, 0x00000190, + 0x00000260, 0x00000193, + 0x00000263, 0x00000194, + 0x00000268, 0x00000197, + 0x00000269, 0x00000196, + 0x0000026f, 0x0000019c, + 0x00000272, 0x0000019d, + 0x00000275, 0x0000019f, + 0x00000280, 0x000001a6, + 0x00000283, 0x000001a9, + 0x00000288, 0x000001ae, + 0x0000028a, 0x000001b1, + 0x0000028b, 0x000001b2, + 0x00000292, 0x000001b7, + 0x00000345, 0x00000399, + 0x000003ac, 0x00000386, + 0x000003ad, 0x00000388, + 0x000003ae, 0x00000389, + 0x000003af, 0x0000038a, + 0x000003b1, 0x00000391, + 0x000003b2, 0x00000392, + 0x000003b3, 0x00000393, + 0x000003b4, 0x00000394, + 0x000003b5, 0x00000395, + 0x000003b6, 0x00000396, + 0x000003b7, 0x00000397, + 0x000003b8, 0x00000398, + 0x000003b9, 0x00000399, + 0x000003ba, 0x0000039a, + 0x000003bb, 0x0000039b, + 0x000003bc, 0x0000039c, + 0x000003bd, 0x0000039d, + 0x000003be, 0x0000039e, + 0x000003bf, 0x0000039f, + 0x000003c0, 0x000003a0, + 0x000003c1, 0x000003a1, + 0x000003c2, 0x000003a3, + 0x000003c3, 0x000003a3, + 0x000003c4, 0x000003a4, + 0x000003c5, 0x000003a5, + 0x000003c6, 0x000003a6, + 0x000003c7, 0x000003a7, + 0x000003c8, 0x000003a8, + 0x000003c9, 0x000003a9, + 0x000003ca, 0x000003aa, + 0x000003cb, 0x000003ab, + 0x000003cc, 0x0000038c, + 0x000003cd, 0x0000038e, + 0x000003ce, 0x0000038f, + 0x000003d0, 0x00000392, + 0x000003d1, 0x00000398, + 0x000003d5, 0x000003a6, + 0x000003d6, 0x000003a0, + 0x000003d9, 0x000003d8, + 0x000003db, 0x000003da, + 0x000003dd, 0x000003dc, + 0x000003df, 0x000003de, + 0x000003e1, 0x000003e0, + 0x000003e3, 0x000003e2, + 0x000003e5, 0x000003e4, + 0x000003e7, 0x000003e6, + 0x000003e9, 0x000003e8, + 0x000003eb, 0x000003ea, + 0x000003ed, 0x000003ec, + 0x000003ef, 0x000003ee, + 0x000003f0, 0x0000039a, + 0x000003f1, 0x000003a1, + 0x000003f2, 0x000003a3, + 0x000003f5, 0x00000395, + 0x00000430, 0x00000410, + 0x00000431, 0x00000411, + 0x00000432, 0x00000412, + 0x00000433, 0x00000413, + 0x00000434, 0x00000414, + 0x00000435, 0x00000415, + 0x00000436, 0x00000416, + 0x00000437, 0x00000417, + 0x00000438, 0x00000418, + 0x00000439, 0x00000419, + 0x0000043a, 0x0000041a, + 0x0000043b, 0x0000041b, + 0x0000043c, 0x0000041c, + 0x0000043d, 0x0000041d, + 0x0000043e, 0x0000041e, + 0x0000043f, 0x0000041f, + 0x00000440, 0x00000420, + 0x00000441, 0x00000421, + 0x00000442, 0x00000422, + 0x00000443, 0x00000423, + 0x00000444, 0x00000424, + 0x00000445, 0x00000425, + 0x00000446, 0x00000426, + 0x00000447, 0x00000427, + 0x00000448, 0x00000428, + 0x00000449, 0x00000429, + 0x0000044a, 0x0000042a, + 0x0000044b, 0x0000042b, + 0x0000044c, 0x0000042c, + 0x0000044d, 0x0000042d, + 0x0000044e, 0x0000042e, + 0x0000044f, 0x0000042f, + 0x00000450, 0x00000400, + 0x00000451, 0x00000401, + 0x00000452, 0x00000402, + 0x00000453, 0x00000403, + 0x00000454, 0x00000404, + 0x00000455, 0x00000405, + 0x00000456, 0x00000406, + 0x00000457, 0x00000407, + 0x00000458, 0x00000408, + 0x00000459, 0x00000409, + 0x0000045a, 0x0000040a, + 0x0000045b, 0x0000040b, + 0x0000045c, 0x0000040c, + 0x0000045d, 0x0000040d, + 0x0000045e, 0x0000040e, + 0x0000045f, 0x0000040f, + 0x00000461, 0x00000460, + 0x00000463, 0x00000462, + 0x00000465, 0x00000464, + 0x00000467, 0x00000466, + 0x00000469, 0x00000468, + 0x0000046b, 0x0000046a, + 0x0000046d, 0x0000046c, + 0x0000046f, 0x0000046e, + 0x00000471, 0x00000470, + 0x00000473, 0x00000472, + 0x00000475, 0x00000474, + 0x00000477, 0x00000476, + 0x00000479, 0x00000478, + 0x0000047b, 0x0000047a, + 0x0000047d, 0x0000047c, + 0x0000047f, 0x0000047e, + 0x00000481, 0x00000480, + 0x0000048b, 0x0000048a, + 0x0000048d, 0x0000048c, + 0x0000048f, 0x0000048e, + 0x00000491, 0x00000490, + 0x00000493, 0x00000492, + 0x00000495, 0x00000494, + 0x00000497, 0x00000496, + 0x00000499, 0x00000498, + 0x0000049b, 0x0000049a, + 0x0000049d, 0x0000049c, + 0x0000049f, 0x0000049e, + 0x000004a1, 0x000004a0, + 0x000004a3, 0x000004a2, + 0x000004a5, 0x000004a4, + 0x000004a7, 0x000004a6, + 0x000004a9, 0x000004a8, + 0x000004ab, 0x000004aa, + 0x000004ad, 0x000004ac, + 0x000004af, 0x000004ae, + 0x000004b1, 0x000004b0, + 0x000004b3, 0x000004b2, + 0x000004b5, 0x000004b4, + 0x000004b7, 0x000004b6, + 0x000004b9, 0x000004b8, + 0x000004bb, 0x000004ba, + 0x000004bd, 0x000004bc, + 0x000004bf, 0x000004be, + 0x000004c2, 0x000004c1, + 0x000004c4, 0x000004c3, + 0x000004c6, 0x000004c5, + 0x000004c8, 0x000004c7, + 0x000004ca, 0x000004c9, + 0x000004cc, 0x000004cb, + 0x000004ce, 0x000004cd, + 0x000004d1, 0x000004d0, + 0x000004d3, 0x000004d2, + 0x000004d5, 0x000004d4, + 0x000004d7, 0x000004d6, + 0x000004d9, 0x000004d8, + 0x000004db, 0x000004da, + 0x000004dd, 0x000004dc, + 0x000004df, 0x000004de, + 0x000004e1, 0x000004e0, + 0x000004e3, 0x000004e2, + 0x000004e5, 0x000004e4, + 0x000004e7, 0x000004e6, + 0x000004e9, 0x000004e8, + 0x000004eb, 0x000004ea, + 0x000004ed, 0x000004ec, + 0x000004ef, 0x000004ee, + 0x000004f1, 0x000004f0, + 0x000004f3, 0x000004f2, + 0x000004f5, 0x000004f4, + 0x000004f9, 0x000004f8, + 0x00000501, 0x00000500, + 0x00000503, 0x00000502, + 0x00000505, 0x00000504, + 0x00000507, 0x00000506, + 0x00000509, 0x00000508, + 0x0000050b, 0x0000050a, + 0x0000050d, 0x0000050c, + 0x0000050f, 0x0000050e, + 0x00000561, 0x00000531, + 0x00000562, 0x00000532, + 0x00000563, 0x00000533, + 0x00000564, 0x00000534, + 0x00000565, 0x00000535, + 0x00000566, 0x00000536, + 0x00000567, 0x00000537, + 0x00000568, 0x00000538, + 0x00000569, 0x00000539, + 0x0000056a, 0x0000053a, + 0x0000056b, 0x0000053b, + 0x0000056c, 0x0000053c, + 0x0000056d, 0x0000053d, + 0x0000056e, 0x0000053e, + 0x0000056f, 0x0000053f, + 0x00000570, 0x00000540, + 0x00000571, 0x00000541, + 0x00000572, 0x00000542, + 0x00000573, 0x00000543, + 0x00000574, 0x00000544, + 0x00000575, 0x00000545, + 0x00000576, 0x00000546, + 0x00000577, 0x00000547, + 0x00000578, 0x00000548, + 0x00000579, 0x00000549, + 0x0000057a, 0x0000054a, + 0x0000057b, 0x0000054b, + 0x0000057c, 0x0000054c, + 0x0000057d, 0x0000054d, + 0x0000057e, 0x0000054e, + 0x0000057f, 0x0000054f, + 0x00000580, 0x00000550, + 0x00000581, 0x00000551, + 0x00000582, 0x00000552, + 0x00000583, 0x00000553, + 0x00000584, 0x00000554, + 0x00000585, 0x00000555, + 0x00000586, 0x00000556, + 0x00001e01, 0x00001e00, + 0x00001e03, 0x00001e02, + 0x00001e05, 0x00001e04, + 0x00001e07, 0x00001e06, + 0x00001e09, 0x00001e08, + 0x00001e0b, 0x00001e0a, + 0x00001e0d, 0x00001e0c, + 0x00001e0f, 0x00001e0e, + 0x00001e11, 0x00001e10, + 0x00001e13, 0x00001e12, + 0x00001e15, 0x00001e14, + 0x00001e17, 0x00001e16, + 0x00001e19, 0x00001e18, + 0x00001e1b, 0x00001e1a, + 0x00001e1d, 0x00001e1c, + 0x00001e1f, 0x00001e1e, + 0x00001e21, 0x00001e20, + 0x00001e23, 0x00001e22, + 0x00001e25, 0x00001e24, + 0x00001e27, 0x00001e26, + 0x00001e29, 0x00001e28, + 0x00001e2b, 0x00001e2a, + 0x00001e2d, 0x00001e2c, + 0x00001e2f, 0x00001e2e, + 0x00001e31, 0x00001e30, + 0x00001e33, 0x00001e32, + 0x00001e35, 0x00001e34, + 0x00001e37, 0x00001e36, + 0x00001e39, 0x00001e38, + 0x00001e3b, 0x00001e3a, + 0x00001e3d, 0x00001e3c, + 0x00001e3f, 0x00001e3e, + 0x00001e41, 0x00001e40, + 0x00001e43, 0x00001e42, + 0x00001e45, 0x00001e44, + 0x00001e47, 0x00001e46, + 0x00001e49, 0x00001e48, + 0x00001e4b, 0x00001e4a, + 0x00001e4d, 0x00001e4c, + 0x00001e4f, 0x00001e4e, + 0x00001e51, 0x00001e50, + 0x00001e53, 0x00001e52, + 0x00001e55, 0x00001e54, + 0x00001e57, 0x00001e56, + 0x00001e59, 0x00001e58, + 0x00001e5b, 0x00001e5a, + 0x00001e5d, 0x00001e5c, + 0x00001e5f, 0x00001e5e, + 0x00001e61, 0x00001e60, + 0x00001e63, 0x00001e62, + 0x00001e65, 0x00001e64, + 0x00001e67, 0x00001e66, + 0x00001e69, 0x00001e68, + 0x00001e6b, 0x00001e6a, + 0x00001e6d, 0x00001e6c, + 0x00001e6f, 0x00001e6e, + 0x00001e71, 0x00001e70, + 0x00001e73, 0x00001e72, + 0x00001e75, 0x00001e74, + 0x00001e77, 0x00001e76, + 0x00001e79, 0x00001e78, + 0x00001e7b, 0x00001e7a, + 0x00001e7d, 0x00001e7c, + 0x00001e7f, 0x00001e7e, + 0x00001e81, 0x00001e80, + 0x00001e83, 0x00001e82, + 0x00001e85, 0x00001e84, + 0x00001e87, 0x00001e86, + 0x00001e89, 0x00001e88, + 0x00001e8b, 0x00001e8a, + 0x00001e8d, 0x00001e8c, + 0x00001e8f, 0x00001e8e, + 0x00001e91, 0x00001e90, + 0x00001e93, 0x00001e92, + 0x00001e95, 0x00001e94, + 0x00001e9b, 0x00001e60, + 0x00001ea1, 0x00001ea0, + 0x00001ea3, 0x00001ea2, + 0x00001ea5, 0x00001ea4, + 0x00001ea7, 0x00001ea6, + 0x00001ea9, 0x00001ea8, + 0x00001eab, 0x00001eaa, + 0x00001ead, 0x00001eac, + 0x00001eaf, 0x00001eae, + 0x00001eb1, 0x00001eb0, + 0x00001eb3, 0x00001eb2, + 0x00001eb5, 0x00001eb4, + 0x00001eb7, 0x00001eb6, + 0x00001eb9, 0x00001eb8, + 0x00001ebb, 0x00001eba, + 0x00001ebd, 0x00001ebc, + 0x00001ebf, 0x00001ebe, + 0x00001ec1, 0x00001ec0, + 0x00001ec3, 0x00001ec2, + 0x00001ec5, 0x00001ec4, + 0x00001ec7, 0x00001ec6, + 0x00001ec9, 0x00001ec8, + 0x00001ecb, 0x00001eca, + 0x00001ecd, 0x00001ecc, + 0x00001ecf, 0x00001ece, + 0x00001ed1, 0x00001ed0, + 0x00001ed3, 0x00001ed2, + 0x00001ed5, 0x00001ed4, + 0x00001ed7, 0x00001ed6, + 0x00001ed9, 0x00001ed8, + 0x00001edb, 0x00001eda, + 0x00001edd, 0x00001edc, + 0x00001edf, 0x00001ede, + 0x00001ee1, 0x00001ee0, + 0x00001ee3, 0x00001ee2, + 0x00001ee5, 0x00001ee4, + 0x00001ee7, 0x00001ee6, + 0x00001ee9, 0x00001ee8, + 0x00001eeb, 0x00001eea, + 0x00001eed, 0x00001eec, + 0x00001eef, 0x00001eee, + 0x00001ef1, 0x00001ef0, + 0x00001ef3, 0x00001ef2, + 0x00001ef5, 0x00001ef4, + 0x00001ef7, 0x00001ef6, + 0x00001ef9, 0x00001ef8, + 0x00001f00, 0x00001f08, + 0x00001f01, 0x00001f09, + 0x00001f02, 0x00001f0a, + 0x00001f03, 0x00001f0b, + 0x00001f04, 0x00001f0c, + 0x00001f05, 0x00001f0d, + 0x00001f06, 0x00001f0e, + 0x00001f07, 0x00001f0f, + 0x00001f10, 0x00001f18, + 0x00001f11, 0x00001f19, + 0x00001f12, 0x00001f1a, + 0x00001f13, 0x00001f1b, + 0x00001f14, 0x00001f1c, + 0x00001f15, 0x00001f1d, + 0x00001f20, 0x00001f28, + 0x00001f21, 0x00001f29, + 0x00001f22, 0x00001f2a, + 0x00001f23, 0x00001f2b, + 0x00001f24, 0x00001f2c, + 0x00001f25, 0x00001f2d, + 0x00001f26, 0x00001f2e, + 0x00001f27, 0x00001f2f, + 0x00001f30, 0x00001f38, + 0x00001f31, 0x00001f39, + 0x00001f32, 0x00001f3a, + 0x00001f33, 0x00001f3b, + 0x00001f34, 0x00001f3c, + 0x00001f35, 0x00001f3d, + 0x00001f36, 0x00001f3e, + 0x00001f37, 0x00001f3f, + 0x00001f40, 0x00001f48, + 0x00001f41, 0x00001f49, + 0x00001f42, 0x00001f4a, + 0x00001f43, 0x00001f4b, + 0x00001f44, 0x00001f4c, + 0x00001f45, 0x00001f4d, + 0x00001f51, 0x00001f59, + 0x00001f53, 0x00001f5b, + 0x00001f55, 0x00001f5d, + 0x00001f57, 0x00001f5f, + 0x00001f60, 0x00001f68, + 0x00001f61, 0x00001f69, + 0x00001f62, 0x00001f6a, + 0x00001f63, 0x00001f6b, + 0x00001f64, 0x00001f6c, + 0x00001f65, 0x00001f6d, + 0x00001f66, 0x00001f6e, + 0x00001f67, 0x00001f6f, + 0x00001f70, 0x00001fba, + 0x00001f71, 0x00001fbb, + 0x00001f72, 0x00001fc8, + 0x00001f73, 0x00001fc9, + 0x00001f74, 0x00001fca, + 0x00001f75, 0x00001fcb, + 0x00001f76, 0x00001fda, + 0x00001f77, 0x00001fdb, + 0x00001f78, 0x00001ff8, + 0x00001f79, 0x00001ff9, + 0x00001f7a, 0x00001fea, + 0x00001f7b, 0x00001feb, + 0x00001f7c, 0x00001ffa, + 0x00001f7d, 0x00001ffb, + 0x00001f80, 0x00001f88, + 0x00001f81, 0x00001f89, + 0x00001f82, 0x00001f8a, + 0x00001f83, 0x00001f8b, + 0x00001f84, 0x00001f8c, + 0x00001f85, 0x00001f8d, + 0x00001f86, 0x00001f8e, + 0x00001f87, 0x00001f8f, + 0x00001f90, 0x00001f98, + 0x00001f91, 0x00001f99, + 0x00001f92, 0x00001f9a, + 0x00001f93, 0x00001f9b, + 0x00001f94, 0x00001f9c, + 0x00001f95, 0x00001f9d, + 0x00001f96, 0x00001f9e, + 0x00001f97, 0x00001f9f, + 0x00001fa0, 0x00001fa8, + 0x00001fa1, 0x00001fa9, + 0x00001fa2, 0x00001faa, + 0x00001fa3, 0x00001fab, + 0x00001fa4, 0x00001fac, + 0x00001fa5, 0x00001fad, + 0x00001fa6, 0x00001fae, + 0x00001fa7, 0x00001faf, + 0x00001fb0, 0x00001fb8, + 0x00001fb1, 0x00001fb9, + 0x00001fb3, 0x00001fbc, + 0x00001fbe, 0x00000399, + 0x00001fc3, 0x00001fcc, + 0x00001fd0, 0x00001fd8, + 0x00001fd1, 0x00001fd9, + 0x00001fe0, 0x00001fe8, + 0x00001fe1, 0x00001fe9, + 0x00001fe5, 0x00001fec, + 0x00001ff3, 0x00001ffc, + 0x00002170, 0x00002160, + 0x00002171, 0x00002161, + 0x00002172, 0x00002162, + 0x00002173, 0x00002163, + 0x00002174, 0x00002164, + 0x00002175, 0x00002165, + 0x00002176, 0x00002166, + 0x00002177, 0x00002167, + 0x00002178, 0x00002168, + 0x00002179, 0x00002169, + 0x0000217a, 0x0000216a, + 0x0000217b, 0x0000216b, + 0x0000217c, 0x0000216c, + 0x0000217d, 0x0000216d, + 0x0000217e, 0x0000216e, + 0x0000217f, 0x0000216f, + 0x000024d0, 0x000024b6, + 0x000024d1, 0x000024b7, + 0x000024d2, 0x000024b8, + 0x000024d3, 0x000024b9, + 0x000024d4, 0x000024ba, + 0x000024d5, 0x000024bb, + 0x000024d6, 0x000024bc, + 0x000024d7, 0x000024bd, + 0x000024d8, 0x000024be, + 0x000024d9, 0x000024bf, + 0x000024da, 0x000024c0, + 0x000024db, 0x000024c1, + 0x000024dc, 0x000024c2, + 0x000024dd, 0x000024c3, + 0x000024de, 0x000024c4, + 0x000024df, 0x000024c5, + 0x000024e0, 0x000024c6, + 0x000024e1, 0x000024c7, + 0x000024e2, 0x000024c8, + 0x000024e3, 0x000024c9, + 0x000024e4, 0x000024ca, + 0x000024e5, 0x000024cb, + 0x000024e6, 0x000024cc, + 0x000024e7, 0x000024cd, + 0x000024e8, 0x000024ce, + 0x000024e9, 0x000024cf, + 0x0000ff41, 0x0000ff21, + 0x0000ff42, 0x0000ff22, + 0x0000ff43, 0x0000ff23, + 0x0000ff44, 0x0000ff24, + 0x0000ff45, 0x0000ff25, + 0x0000ff46, 0x0000ff26, + 0x0000ff47, 0x0000ff27, + 0x0000ff48, 0x0000ff28, + 0x0000ff49, 0x0000ff29, + 0x0000ff4a, 0x0000ff2a, + 0x0000ff4b, 0x0000ff2b, + 0x0000ff4c, 0x0000ff2c, + 0x0000ff4d, 0x0000ff2d, + 0x0000ff4e, 0x0000ff2e, + 0x0000ff4f, 0x0000ff2f, + 0x0000ff50, 0x0000ff30, + 0x0000ff51, 0x0000ff31, + 0x0000ff52, 0x0000ff32, + 0x0000ff53, 0x0000ff33, + 0x0000ff54, 0x0000ff34, + 0x0000ff55, 0x0000ff35, + 0x0000ff56, 0x0000ff36, + 0x0000ff57, 0x0000ff37, + 0x0000ff58, 0x0000ff38, + 0x0000ff59, 0x0000ff39, + 0x0000ff5a, 0x0000ff3a, + 0x00010428, 0x00010400, + 0x00010429, 0x00010401, + 0x0001042a, 0x00010402, + 0x0001042b, 0x00010403, + 0x0001042c, 0x00010404, + 0x0001042d, 0x00010405, + 0x0001042e, 0x00010406, + 0x0001042f, 0x00010407, + 0x00010430, 0x00010408, + 0x00010431, 0x00010409, + 0x00010432, 0x0001040a, + 0x00010433, 0x0001040b, + 0x00010434, 0x0001040c, + 0x00010435, 0x0001040d, + 0x00010436, 0x0001040e, + 0x00010437, 0x0001040f, + 0x00010438, 0x00010410, + 0x00010439, 0x00010411, + 0x0001043a, 0x00010412, + 0x0001043b, 0x00010413, + 0x0001043c, 0x00010414, + 0x0001043d, 0x00010415, + 0x0001043e, 0x00010416, + 0x0001043f, 0x00010417, + 0x00010440, 0x00010418, + 0x00010441, 0x00010419, + 0x00010442, 0x0001041a, + 0x00010443, 0x0001041b, + 0x00010444, 0x0001041c, + 0x00010445, 0x0001041d, + 0x00010446, 0x0001041e, + 0x00010447, 0x0001041f, + 0x00010448, 0x00010420, + 0x00010449, 0x00010421, + 0x0001044a, 0x00010422, + 0x0001044b, 0x00010423, + 0x0001044c, 0x00010424, + 0x0001044d, 0x00010425, +}; + +#endif /*UTF8TABLES_H_*/ diff --git a/2.5.13/2.5.x/authors.txt b/2.5.13/2.5.x/authors.txt new file mode 100644 index 00000000..c39e7286 --- /dev/null +++ b/2.5.13/2.5.x/authors.txt @@ -0,0 +1,7 @@ +b1v1r = Brian Rectanus +brectanu = Brian Rectanus +brectanus = Brian Rectanus +ivanr = Ivan Ristić +rbarnett = Ryan C. Barnett +[anonymous] = Brian Rectanus +(no author) = Brian Rectanus diff --git a/2.5.13/2.5.x/doc/Makefile b/2.5.13/2.5.x/doc/Makefile new file mode 100644 index 00000000..e39b8906 --- /dev/null +++ b/2.5.13/2.5.x/doc/Makefile @@ -0,0 +1,45 @@ +FOP = /opt/fop/fop.sh +XALAN = /opt/fop/xalan.sh +JAVA_HOME = /usr/lib/jvm/java-6-sun + +ALLDOCS = modsecurity2-apache-reference.pdf modsecurity2-data-formats.pdf modsecurity2-apache-reference.html modsecurity2-data-formats.html html-multipage/index.html index.html + +all: $(ALLDOCS) + +modsecurity2-apache-reference.pdf: modsecurity2-apache-reference.xml + JAVA_HOME=$(JAVA_HOME); export JAVA_HOME; \ + $(FOP) -q -xsl pdf.xsl -xml modsecurity2-apache-reference.xml -pdf modsecurity2-apache-reference.pdf; + +modsecurity2-data-formats.pdf: modsecurity2-data-formats.xml + JAVA_HOME=$(JAVA_HOME); export JAVA_HOME; \ + $(FOP) -q -xsl pdf.xsl -xml modsecurity2-data-formats.xml -pdf modsecurity2-data-formats.pdf; + +modsecurity2-apache-reference.html: modsecurity2-apache-reference.xml + JAVA_HOME=$(JAVA_HOME); export JAVA_HOME; \ + $(XALAN) -q -xsl html.xsl -in modsecurity2-apache-reference.xml -param base.dir ./; \ + mv index.html modsecurity2-apache-reference.html; + +modsecurity2-data-formats.html: modsecurity2-data-formats.xml + JAVA_HOME=$(JAVA_HOME); export JAVA_HOME; \ + $(XALAN) -q -xsl html.xsl -in modsecurity2-data-formats.xml -param base.dir ./; \ + mv index.html modsecurity2-data-formats.html; + +html-multipage/index.html: modsecurity2-apache-reference.xml + JAVA_HOME=$(JAVA_HOME); export JAVA_HOME; \ + mkdir html-multipage/; \ + $(XALAN) -q -xsl html-chunked.xsl -in modsecurity2-apache-reference.xml -param base.dir html-multipage/; \ + cp modsecurity-reference.css html-multipage/; \ + cp modsecurity.gif html-multipage/; \ + cp trustwave-logo-small.gif html-multipage/; \ + cp apache_request_cycle-modsecurity.jpg html-multipage/ + +index.html: main-index.html + (cp main-index.html index.html && \ + VERSION=`grep ".*" modsecurity2-apache-reference.xml | sed 's/[ \t]*<\/\?releaseinfo>[ \t]*//g'` && \ + perl -pi -e "s/\\\$$version/$$VERSION/" index.html) + +clean: + rm -rf docindex.html $(ALLDOCS) html-multipage + +.PHONY: setup cleanup + diff --git a/2.5.13/2.5.x/doc/apache_request_cycle-modsecurity.jpg b/2.5.13/2.5.x/doc/apache_request_cycle-modsecurity.jpg new file mode 100644 index 0000000000000000000000000000000000000000..390c649350e15d4a359ca2be8ca15b4a963cc96b GIT binary patch literal 92271 zcmeFZ2UrwK);Hcb1j&NrG$0@-Ac7#70TED90ZEdxBmqg183X~zf&z*HgOU^h$r&Vr zh~yjxkQ`=+0}M0&_TIhsdiTHK?tc63x8M6LJpFX{bamCK>Z()c{LZfmKZ2hDPTf$s zt^yDc5CC4_KY$+xlz@}O#3aN-CrL<1NJ&qUQP5FRkdsp|)1IcLV`qVKu(PnSaq?W? z<2)nC&Bn$raZd1}h?tldlut@dQdIWBB{9+OnGleYl2VXUFi}!6iE^=ViT=ZX_)h@M zNeGJQEQH_;aDs*aLPLOW2A}{yKmnd|K0-^`Yu9gRY3u0f-PJd+w6eZuV{2#c=I-&()63f@_;E;R*pu*x z_-D@(5|duMOn&n=BQq;I=iU3F;*!#`@`}o;#-`?$*0%PJ&wc#^gG0k3qhrX~x%qDk zi%ZKZ=&kLY-MxLx!QuCM5de^%tMzNm{;Xa!V7*Qd5<&<`zSoQ3geQ1FXb6e8M2Kk> zHA&1}PM;Bdbdv5$TzX*xDfgv2D0&OmUNQzAF(fbgd)0ob*&nM|&_7kPUn}-oy~Y3v z2m$!ho6hE2hW&3Tz~enCKGegz_}O zPPR4rOxHH%{Woc->0_q?JYY(RT|$um&afP&LDl1wE8(lv@Jq(E0G+1te~+zs+(91-Hjin- z!AoFR$SXV$)BC%-VMXQtX7GQZ4F;w-20X||#T|^*)>`M(YZ#8!;}Y1IES|H8@Ia^L z3hc-7;Z4(9Y-pR~5KBDJCtv_9{n9Vb#p8i@@cmozFNF`2@PK;{3CRyb@GY|baMBc4 z$9mAKz#z|v2inVpk3F3i9Ln>#k#KCJD;^krh`U$q^Kb3rzsUaQ=kw81nKnSYloJgT zq(L%wa3+nzz3WPI)BDkhFYma`2Ppwvl+JnyoUv5O#F{4lK{MLqz}er?{aTRK)0pbp z4#<<6`E#$k*qb8-Jj}escc~lX81{yn@W7a7lIV?L?!d)Nz^9Z>Q-SE;c(CDtsQP&l zM?7F|EP)5q%=3dP@xYJ6_70}LS*bCAzJ8cxWFk^o_hQ&+wIE|YmGq$o@g#ktm*K2+ z!4oWfu2+N_agnsm%SA)k@mD7wwMjC4b?GmKQr7VlG>Z+qbWiMWiF;z6y)Cgx0k0C- zhZ_JJOIhLEqD)Qi9*FA6#x{nQO?BM}|Aq zbWK^TQ#`1c0}fLYI)z)hPj$VK2p7VVy6}Z5u^F)r(mp3)Epz-TD%V!;1MI#(9~%2% zbTyebzu?~DTDb_RcEy;sC*_i2JM2bPOcB48*=L_quKWZ8TCyX}K`;@&Cal!rR_6BU zFI)ThSQ{trw!T1i<%_qNr^8RrA5C982&hZY(jMUGyir|M5@<4^1i8gtbd9d_(I=8J zpL>DAnnodQv3VuqFlxBN-7cFP2R9y(F=y)e_)oO!pFWYuOg$NW-fpy+spntRpAG`hyu4SCB(weLd--=Nd3`o*i^u3Vh?lJhiQ{Z(%O>p2Bd zeF5i;uFoEsQh7x-?kCd^&cN<@-=ocly40$`@uIz3a<|J7W7*n^iM)&ENZqsOu`N%t z7Ad?4?^@Wh{5+*faJR>LV8H*oY3Pwb~TB z0FyTrv0?QzTxjfAr=dQpuOV|BS6rIV`LZ<;rh|aZo2jMaJp@L0Agq2~YPS1`tPKxj z=$7{A!H}B=R9?ln#zH)>OrHn1|7rpKFPO=7d^^V-PEzdO`Jfr|VNxK~`N`C270y-N zQH~vgfnK8mtY9oDyvJ#J`z?+^^>O`@g!4fUiH^~9jM%l^Zt^LGcIIZQELESdU!V3# zG&`S^PH?mz32tCUP(Q=MkmHfv%ng~cRtc|YDIRwDr)1a_+DRKRXi)Ij6AN_g*!fXs zdCeXf<3`KRN$`gu#3wlamjZ7c-U`-pFFq?MN%F8CoH zNP(h?WO?y`Vm170p!ClL#eFYm{C`V9iBOsvo;HX<8Fm#>SYJAjmpI#Joyf4%+0#Rm z%>O2k?C%^>zgOvZ@cJUW=bfW6FwjqhmYvp0;lg02UWw~4}MB-C+rNK zs~E}zGF+w7etMESu6>>iILN-6ysDtqO=)oDA*T3tKTmm^4VO~fE?3x`;n>Z66S`iU zU<&h?)CZeTCiZ|Wew$lOJs}<4_w|a5yiPZj8^lDz#Y0z9IK*N)u*0nM@=LPVkyh_6 z{r(!hw|1UBEnTIE=@0TpNNz@b^)9T?Q<5(e?hApk>c*(-Ff0rOzlDxT@)hLZ8g+h< z95hiZ+gHijpD)?@5J%-Zls8y`9&VMBxQ;fuaNnYrc17UyIc@Kk+LfO;7kR@9+GWVr z*XpRf&NpcrE|p|ivL3`W2yURcZKO`#hRH?@qSXVja&wI3NqWWiD#~ZiA)mH!nr>{c zs}?Rli@FymTi!@xdq~|3ebUp;Th+UMzwt(}`-d|SiWJu?Q3WQE8eI+}qlrnm=oc zcIDa#|E)!Y>}z=GPM$Nxr~?NbnmU5@cE4HQhoo!1kNZ@zl5WvA@}EB<5QO;491HY& z#1eV=8uhI2I80vVw!Tj?+9#VH27#Ef#hgoJMyZ4#&sIEBD|2ykiU|`;dTQ043=W9 zOA)*t$htoNT_6QAi- z^=!~@uy>lDL@LMvCvxVdhic{9ko%z}L9&ei&-)_$qaBNH5c99s#m86DsQkopy`0(5+j>O(oiO7qP+H_7WqyF3pU0>y9? zBe!uJ1G4HqCu`6|Yf1M-qEwhdm<|(RR|FVm`|R&vuc6M^omQkmn79xok=+pRd;O6; zcW6xz(eJ+Mz-lc(RH8;__WJ4ji!eoPabb#OgR;^r`~9+Y-i;SuhVpr=PZ~0}laL@* z71-p{Q2Wh?Y0>V}G+y1t#WlFjP6I>zMja)N`KKaZsa+pPuXJgb<`WA=ImX=xjTPX1^ixzG(ZG06CFs<>i2$a=R z8`?Q42&@tct?-a}>yjg$!7$|ZWNbc+S93Cqv>h6RW5jZqO>tm`DUTLi46#jNx6QL#L{d4=`lH)@iYld`pEG)KtX`H^2G>g+ zSfxVWo+yfZd4b3C*#pj`j)NHLfRr3a<6@6>uh`L~nm%fWgR>tu&{zJXBt`F%@E0mq z(%sgR>^?i<{g|>9b$UM~)YlhPj;*B`@=@ha?Y1Y6Uw{3=y7-WfC=kGrOjR#~dPCHq zr1V4$mSA^_SvB?&fh1ysdQLTmT+#oFt4otvF$GOTnkwq{BgB%Yk4l>Lw{a5z@d(MNqzMr zY9ZP=V>yoL!+HZvx@)s_B!@a*ye;I5JXww@t)GGpz(4o^Sl{k6slQtV4H1J=8L#L` z8y72G$dpKPy%{w=%`?_zXS$)t&04I)}h@n8~U+0^jF)#Hj zHC!ehm=q&uz=BSMVJ_&mHNmC%e?VR|h+;$Vz?M)a9xxt1NM2ukf-%JdXC~@*C+lD4 zPyO8)?AH?i&P?~uq?0U&Fx}!cPE&?`t|>smnR`{@%0tINzFeAIwhhj0BscbvP4*hpS?z9C;oh|$ztCMB#Q&VM}n^+OK z>7zjcI<{n&no`s9ps2U9Qp<f@nTSzl6QW%#b|4EDly zR1e8(&{B&+EoQH(V)?tNd;&_BJ4-*E{}kL4(d^8yd_9*^Xqu5{1~L^W+5IOWzT(be z<@##QxmfZhDn4XhoT_`)O6fJ+X1bf~dDH~jmN{NZIOSV1xJt6|59%l3?eDVT!(4{$ zfTl9tPlw)DQwI0;Qh229PG50W>2{?e;6&N)L$^^`}iLxtjZm9UAIl zn|~^{4>Tp`K2z@#$p^}|;pl9_7hY|8BwUH-F)`w<|6R93WWmXYJz8kDwO$ z|21sRAN>)5wu#nilAcP-PUxtXq#wG9vpUDO9}12GJn*wLZ~r9R&I&)|3;bXB+lI_* zO$8_ndjn(m*2}GF)!X*uvxm~dSy*0HKXQ8#OMN&CNG8xw-_O-UN?@N1O@r>Q?Oz1% z-Wjd37^emdzSD1rsk^^Yf$ai}-+W|k#WCpe*Kze*yEI&L(p4epC%fLohdt{f1AoU7 z-IhJNQ~gpAt1AlQcuvrx3A$NWUP17)Z6c3dosT?d;7hFruyXNA*r3;q1K8@q#wdJ; zlmi^d0%=D!kyb~^XX8%pCTTxSJNjnUwyK&mM+*P8s#FHjAndZ+a93~k5d6+Wv^QnM zJn>R*EI*txWR0_Z-+Jmu!a8WTq0RJ0Bbqm+mLttZ2v_PTEAW1KJUyuGGv%vx-z1BR z58zZge&%=}ULAzOhxtKeVxd7SR!`@rP8?D7rJl+eR&2lpOXEPq9u-MAjWJ5LiBH?I z>?S?I%Gu@VUm{m~fo<6)CYyPxcz8+`X+k}FX)cc%ITbFx`6LxiZZf7w*&n9Xm+~??~K=FW3_|euV?h zQS=g9i*i~sn^#y-Ynccmec3G=>$uTU{Usw}uV^xAmNPG8BstPI=KN$++4R@&Wb?45-S7(flaH|o^ycJ$>TYn3~2RzA8>PzRT0?bPuJ;DUI6Rf7* z53ot9_1qm#g`d~wiQ%2(eorwc=wd2t$0EK_KO=>`Ql$XCnE2Ey!rz+fV#fl-g(36U z+wj^Q`XNl`)gh1WQ?|*$hRgD2ljY99N|p-za?tJCmp(^)5T#N%+1<}1MzB^jRBDY_ zN;0Tjjk9xRj*Fmdjy0c1z6H?_Zf2oO%yY^-QypThZ9&d&{+i8mp+;H+u>vQlG0keq zw7)FDBHQX&SX&`VZWp64;x%I)dUAg;&&RVdn)P;H0Q)lSh~bmuy)l7TK};!I&w+Bj zm=q$wFtXKwulZdK`yMTejBuHMZr4oIqjD$EAdANXtwC+GDoIG{kBky~d2V|JGOU-w zB}dECDu=lX%H=%^>{t@Zj}#}zmVM^O#6R`XcYP5dCDqUq2vRkQ{rtAL*W(uTUJz=s zEU%zD@8+RjpWyaYZvJX()(50>oN29rat)c$M%>b2P7&&7Uw_TXObNXK!?gt&x>^Nx*x>w$a>`V zqRwh?Oy%-ejZIULy*%T9JomU=S)|!sm07X?t3$E0q5M>OeGMYsAM@c@y)gh9wg-=G z?%;|1FmYZi31;hRV|jt)GIIk{$8zgL4@ob}y^Q40aO-zPPZ(CFr1pw!?(LE=&&j#y z?q?^}yRNl*+C=#bcUSdP4Po_XYKi^N?fT2ZsFfZh+f^Tl-r~AK&LH=I&Sib~-OR@U z_cRd95j(I59lv>%+QA|@$XGm`XCrEzr$BL-UA}~1Qw@Z&bOvK2%-GPYS8lOYZq2K< z8GoP>XfCNgF`|gFIl4Wr5z%BYC{{7&a8zB);%E`Y-65%2dIha7&r%-Tn0gU4>kx42 zl}cr*a_G2b-{yXk`&~e`rGr9hkpvUV&JS6#|8cTk#LD3Tu5JeSh%W5-OzBTJB{jU= zttD-bJ{AvL%d>?b-bC;N-;ZDHk;7kNa?mFDZdLuUpTzc?*ibyM>H$B*I4>##cYkMv zRQ%pXS^6ojU-JC-obBRNeE<)7##gB?u(veohVGYaCU1SS)ZA2H5fZZ>*B_8b7n}7j zHih}=qcc_oJ$;A~H_fnLN?VrTG+5tv#+julUy=kLTz=FS2sYR3tGNbGg_soNRpWPJfA2g53MXPX!03Ug4^efsGnPS3#^4T*@)i+=3cuqK|s< zBu^N7-900Jq0?6nf!UKtYN!fSxK6`1TLI3}PxJO0Usa0)Z5UrW>>;sF73s6?XVlP7 z`uuQAYEyYQN~}}-QRV{%%Rslj8i?L@p8NEgfJG+{Eu)it*M}qyPSoU@(-*6v*a!D_ zS^(es^{3pKoH<4}H10DCr3whNLPuCO_jJ0x^`GoqV2J)mS|HdjMlP@!Ql6aIeyYM^cro)5shjF*Fxt{CAVm5YsvnAc_alAA|aRVrA^T z9gFYO&Bt#%PE@Y5&YFgmhVftMVIL~JIZm^8C{xC-Vmb%TLYJP@k}-&gi3h!x}- zsN**8!&f)C2=(RV0aDx@fT@Mlj_kGkkU! zdyamx;Y;(DgR{=inwYX`eF~SJBvG{YVwiYtJ-MG;(W{$%X&Tp%4RT6rj`%`gJqea-m0*UaQ zN7-_i>S%=G>)@dpL&aXfU0-1ulc~`72icUVp_S7qV&k{ucwC&h%|m^*71&jqg-_k} z3Tv(7h;M0n|FHw>FzHBgNMX7Wfr2sEGO%1IM_O9FV%|%X`s&a#-6lPl^_Kmy30v}A z0MgCe0=WBxMY{W6P(Q!2BKAyjOkJGg-5fi&YH6r%`0#!cf8~6{mekIwqwnl}@*M6p z%Zj$tPy|}(9VU!2z0kLvla>21XhN?0n1;L8nlx+*ZD@UtvOZYWe~P`@!~1ehfH3O8tA~J)VH1vJAm6+^{_B*tcT{R%A11PC%nwRca(r9d+(RJu67gym;G5YUQ8I3< zLeA*2@U{9`>YEhDS%3LWEona|S!726`=IO8Wz^TZi))e7V~Jy)CFqv5C(RPiCy#R= z1I{?kC4+zrL8}TC|8X_QCvo^4v~EJ?7;kgZ$%fV|!Cjzdf;`e2+?mv1KciNO>$x$p zxUp>*kI}kY-fua$fr$ThW|i1S2=IW?$aB@!hYA&lx$2B*Fs0qtsNs5lxfogP&~~&I z>^W2u)|TWW{|>d#>MsxFecWIIQ!=TXso*Y0WhS>)5c8f5lroKmZ}IaT4ZcIAC&-dI zeQnToy)Jp82qzAPEfHy(nyCIJ1BPs*fMKnYO8>@&3XnKE%3~gT<EeKqZn_)xxg_Q1z3nYUuiP$N+b^N0+mkNs2HVoaS$Rcg4COi2H?=QzSI^{Fs2 zSRq!C?{Jf@J#Ba0k`}(?yZUp!7!@Z|oV=`hJ~)Xuaz)|+<>nH2BReSAYKjHiPTUt1 zERGYJ`BM-d9I~)b*5ilaHZCCL<=mnT?x@SS47Ctu6I--2=FYVI&EU=47JardaOVA+ z5M?ri8E}zEh)#n*`R9WK_B(ImZvfI?(Mj0A_4+k|EHHReN4Ig9gFo`PaVau@t1~NjrJU{>;KY?w4GN>f$w{YOiOgj){Rh&?n}Sy_Crp zk=C5+)THAJFBY4!G;4e6QZBhB)xz5rDwbP7H|7;@%Ur5wllMu*@H03ace z3p47q{EjpGc!qAev-(n&>5U%gT3hlo@7sLc9IEo+hwxw=t~pa=hw&8ayJtRfY^izt zairqYSGTxF;BnVLxN(|n$snaJ-PumIe+&<_m5=3f=#=9Dm%B6Yn|e0CCvDMpV|;N+ zsh|h`@dNx&*}<5|4ab28o-Rhh)&!uqID;M%#Xt3Rf~?+*cLqwrelSp>lz3oh z1&q_6roTo2g+DUITz-lBu{Li1cI6cHqtG!-lMvz{I9d}oJ`md z`I6yg>`zC~>~}{U^CmxhEwoUc2;Tm9dUs%GvqkuT)(J!~VV^(N5kWxxb^1HT$MGn4 z+`m|Ebe%yRJ>WWA{A7WDpi25ADpHSg~`#y;;BU$mHerTrHuHQ|HfPDXJYUuBO&xf#AQlglkY7CwhqLt%qMJFx&zM921#FmAT=8u@;02f7 zbIeN(t+AQQk-zsEJ_h|7C@0f+5{5k8-wONoG_OF&UjW(&-8yeKz1yxk30&$ly&+q= zlijOu%1FP#gEYdcsnzFQ&RR*eklc+iL)BAq4y1!T4E?_dL#Zy zOD#cxqmDUC7WHS5+Xv{WKv_JX(OViz?WN=1MJ6oUUg5HwS{jr_?E%H8+`ZT`UsL3x zmmfWc5c50e79;5P;#Nz$iW<=M_8)&+0SikRbQ=ge{spRyy4-1~ehW z2%0hV4Pngz&=QStXTctCxaM7XHq>T>b3S*Z;qw*;Mpk%@z~IHvuT-`&-9|km;r1p( z=(B0>R_ro@hCFNh&$nbZ3CtmFLd_%R7cOm@e36JXP*0NrqfeY(j4eK)c^REKT()cN z^TSn!3o=$JE4dNRgg0-EO@#)^_IN32f^OI2NOhn@#VY+C$LsdM5x!(VP+ z6YuisZ`8yz-BOd9Owb8-QD8e;J~OGL(_bP7zkaY`QKoSkm77YA62jD_-5F-Q-%&<>*a%YGu00mi?r|*RtNNxa4WP*`=Y=~zhyksVGveh#finL@G~AfT z98)#5uy{q&&XM;^p{f3aw@6%NqRfko`%auT_Nlv2EYGm%j=*Z#elryN@F&m|EI4@; z7vk@R!vudPD&hfB@!?~#!^LAX2_9faPz6OSg0|_|D$utxHNpdRoFG6B8cer9$MQ;D zB_8lo1P8%)!~(p=T+gpx(i?_tK7nD@LCek<-cyhdpFRagkAY0Od^nEQq2Q2yxBF<8 z8YiRrUt#{0Edw_H??I$L|FX!(RSqdWdkd|z9nKy^$xQi|H~EDSu{;8& z)?nk?y(`}0pF3tLEln#fE)A{Uk8F|+4$Wn84yM?O%yYU|_3C}p@z|PKX$(^W4ZV(! z8K|}`QMjh{D|-j?$!o7a@(Cob=B**h6h1b_JvwfPD59f!UIIK)$owG)GB3|F*6ocX zf8jW5XJyTKez$-Y6W>Nie)|3=Ka9 zF4S)Zt73%;z+hX?iB%j%d{bTL`7?|J1^o|BKRR&n`x)8WYdMKi$$(kFLT#J0Q*c(hq+iEWWv= z$V_soH2@@<4c2fu%SL--Wjq|N^;`#Ix;C@ZkA`_S4S84i?h(+u!~h5--3Ry}DZh1p0Hn`p0Ya~PNlHTA?I1{GtG(Egn}%hT zY~EaDSy1Sgm{6V{x?Mfn?DUPtY-OM8TRpIOOQRVsO66uplxI(i>sv85hHl^#w)b@Zm>s9;Ej5J)*wp9+|a{e z(gLsBce8PWJtHmNQ2A@fyE@apt%zt@sZqW(FMk8j>yztn)Gy#8;c(Em!; z7d71##w};}^WuR!cwi$Ln8C4vr0+p6-h`}Aq4WEZ8grtp5f2)5V`yUl*W zpjqo=dblzDW!U+DPJY6fd%>UHmot*m2@A&1(lOtY3jqZ8$>K_48n`tREypT^ESo)( zzxDKs(aUQ}#=G$Tl~pMx8yd8ph{@PJ8+Z-_ zZ(gYV9(2IgxK1a8$ZozuTGiM zZXAs)r2XO6p{X(3k{%YBr6yN=FENHV?%HzwPy;bG7Vo*ZkD!M2)Tn!8Ps6QN@qpmV zW;c$6DOG+>w*;6+tpjzlJX`tD`i*Pp_e?a~NW{CAlGhi*Ht;}^N6i7`$p$Nej$mm( zPsm{|k7Kp@+*`v5!T68uW)AOuph5pahyEWv|7m4S|1uaa|Gv})cmV$qG?M?Z3ldoV zZduLj@7CP>4~c%BG?R?#Hl2t3bD;Cp3uO4WgrMeNL5FVVQngKH`zuZA6v_6Nd7d5Z zUMS7&p1q}ETcvx`7Vgh^l%Z$&c9~&JC)seEQ-kD7Qze4L&OoQ^rETI+;bR4m&>#WAL)gI1GT?9sjg$D*>A4OUoA z2^W|I-QS4CnD~Sp6I_68P6EEq_|3b#3tDnYz1>M-Z+|=;<;KYt0cr=N_X>1}5RA6g zyT_ti4+e6VJc-mbzk%_p)4ip)R29&I5=DaVM6W>@2t5mmb|`h_^i;|!RVFJ5EI@7v zj0RKG*_`9IN!10}vx+1UcWZ>7E~Tchb2LlVnRr6|S)UT{7f|-PY$Es?t5fq*5*#8LqT2std~z%;t@ezXB?M=<4A1bK&TipWX zzDA(Bf2$xARkKTdc)|!iT?On*3pBxgsMo)vz{c)2!(GC%$OpiNW&R>^Gqx?-JfWsG z8sttOs5X9Gx$rL-s{hHu@m&76ZXhf7+&6ZI5VT6E+td4IFw9^|hwS1xnuoDI z9{<#WWqP7c`K}zQxv7jqMLO$l`)gL)8uB8^0N0&3e#>pocRzAJ{?;{0IQ&K)TKyVT zdML7iO|NeF5%xMrF?*K*ui=M`t*?EE{-C<13))g#sRSV0uOExP{r$P|^YWsDA9Hl?ye zpJ}Na6ZcO8k)05?p|sNr6&M$nStaMNCv(<-k9lDc^w%=vTP*Q_{AE0_zXlKSGEQo9 z)Z}LDU)tD{YLa9l+iAzTIpkn2-wJ4UI+^jDJsR*h+u9EA{9Yaud3{en_+SeRGSpjQ z1;R0IbCt4o6q`KqritSYHLl#c_at?g`Njgdj_6~t2Zmq>u@Fb+AJK;M%fkbkBOtr- zon^tY!Rt+;gfX@AY@)$^B_I9yPf!=$#RJ_5Kb4?5@&Ug1c^Y>di+YE?9sz`{>B6?Z zeQ%yNjOLyI^q11l-v>ebA0OU{B!G+aB!}7!1Sbi9qMdX#7Ag9t+N|PeThpHcjj{g8IR~ z(w+HFB`(R5eI0O)|J-Nzk_m`RAgRoauYfRr+HeRQ&WAQ9ulcZ1G&033#Q8w&0d>le z&Vp6i)!|AJNGX?7@Ko&yp3U)(?}skCjE%Pu+R|&7&DEML|6wEUSM&O(Rk7N7Wb4e@ zE4Bn*BZal%mTv?6pP1;`XkN?`#HjoH1J)*}HF%&A$tu)nvi+Ck&(Fwb)sW0moP|b6 zt1R$FQuL727=Xh*1Ey#~|9!c7em@?VqTeX1M=w+N=!N2XdZjLlhRD!|s%HYun10+f zHt22xEbl?G86Jp)_~Ouc(+uEpol49l;#c&a#XG|F%;Z`f7}cX^Fhmcn?xeqp+DA|b zNwg%(!pJjwQXng|C`8Ocbv?1|ezHWvx`GLp*2Q}Y{*>O=QD``+;_S(1PwqN#j|^9i zdxb(zVL9zIazvC$1HqL!aVTn>oG^Sfy+9kS^Q|Q;EPtUa@(WxjR8F$sJmy40p6~;= zqb8HK_kr$_>z`=m%T3b})Gu)K4pj+BQ3V`KH^)aKgU6p4x8|X%wTsc@b{cl}Yqxt8 z7R9^$7--6)JfhyGy(~OPHl@rs?f2m2*{g9^!@k)A6GH)__EmDFHPkk^SKR(z+#(FA zW2Ez1>xo}oPAoilK^WKNW_sE$T4s(tp=H4Qe$zQ_Hj8&&ksAHG)RC99ma{~N2cdj7 zdq`YnVbNouk@=n3s@Vb|H$x%GeHeeV#bOJ24(qB%%goWDDNU*d#3t3|Zl}p7 zpE69+xV+N^Xrnnw-x*>}3}4KhwVfxj+$~T<$3}6L54tDT^}I=3m}Bjv(yes#0`WES zm988468XvwSgUi;n4MB4%}0+ESULDHZf|TeDV6yQTvZ4 zh4UI34~L&)ZeMe+kH|fn8xnJK%}!cpoitz&ZKzvO!nNKc@g;>Hl0Hx;w~|<%R2H6f zVTmcnOrS@e$!i79Zs#^(bmdGu!x!GMU%hwK!F4KOH}(`asE$?j9+Vmh?Np<5#DLWz8AHH zkhic@^ozAhc6M)FkU6b`<$BYRx!kgGGTu7|b^J;EMRkqRY|8Yi*hoF6Lq*WV_W<-f ziU_z7S?Y582i*J5cN+NX_c70b4l5d!yL(v;+&5lz1ACcYT+zdHs1s2Hj}*xNSff7< z6tyL=&)B@wPAl?gqAxrn(eZtS=JZgW21J8if!Y9N8V=ji5sTdq@%`q-*j%QK$;`O) zl<~oQ4@spVyzMneMC(NJg^F#`EqB}Gs8-N4?BpgMhCD5aJU$Dnf<4hr8@p_}y$wCy z;fHkO)e$U*=QyX@_-eRvcX=L$1?^N%lfxV33)f3pFA6vusdVB3KntlZ1p3=sFUm2V znTD7<>ePf?#k*Nccz|kWo$+eg^}zDcg|9RItM72VMc_t^FF-RVU3D)y35#vR1O8+{ zBPu&+O_rPOoz+R&%eUOMTKUQo8IFq1X<-ITf6e}fcNN&`L+jX=Q<(;woE-^hZ(nzA zjT8>^v5pN-Gme6m6n2OYz->q2-CyAQJ#T*#6>zH)#K7NT>Mf_*js|gDLmxi`P%N=L zOlP0xip=Ir-UMUt_v;rIU`I=u$8W9i089il&aMPXG1oCKU$Wn_7tHR8dzk!80ppyp z#K>6m>Ew|?0z5rG_qQT`XpJw=Wz1x~~qet*>ubwmp@=%(WNCZ4k zb86LS?EuaP0sYK`0raqNS)y#OS32bb7aF$BTc;bVVC$^H2M4jZL%LrZ2tg�wdc= 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%0ANW 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 literal 0 HcmV?d00001 diff --git a/2.5.13/2.5.x/doc/html-chunked.xsl b/2.5.13/2.5.x/doc/html-chunked.xsl new file mode 100644 index 00000000..bd47f88b --- /dev/null +++ b/2.5.13/2.5.x/doc/html-chunked.xsl @@ -0,0 +1,293 @@ + + + + + + + + +modsecurity-reference.css + + +
ModSecurity
+
+ + +
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2.5.13/2.5.x/doc/html.xsl b/2.5.13/2.5.x/doc/html.xsl new file mode 100644 index 00000000..9a81181f --- /dev/null +++ b/2.5.13/2.5.x/doc/html.xsl @@ -0,0 +1,21 @@ + + + + + + + +
ModSecurity
+
+ + + + + + +
+
+ +modsecurity-reference.css + +
diff --git a/2.5.13/2.5.x/doc/main-index.html b/2.5.13/2.5.x/doc/main-index.html new file mode 100644 index 00000000..ab91a5e3 --- /dev/null +++ b/2.5.13/2.5.x/doc/main-index.html @@ -0,0 +1,35 @@ + + + + ModSecurity for Apache Reference + + + + +
+
ModSecurity
+
+ +
+ +
+ +

ModSecurity for Apache Documentation

+ +

$version

+ + + +
+ + + + + diff --git a/2.5.13/2.5.x/doc/migration-matrix.html b/2.5.13/2.5.x/doc/migration-matrix.html new file mode 100644 index 00000000..707176d6 --- /dev/null +++ b/2.5.13/2.5.x/doc/migration-matrix.html @@ -0,0 +1,74 @@ + + + ModSecurity Migration Matrix

ModSecurity Migration Matrix

Version 1.0 / (April 10, 2007)


Migration from 1.x to 2.x

If you are already using an older version of ModSecurity and want to upgrade/migrate your existing custom rules, you will need to ensure that you properly translate all of your Directives to their corresponding 2.0 counterparts. Some directives have simply changed names, however some directives actually behave differently so it is important that you also review the entire 2.0 Reference Manual. +The migration matrix show below should help you to translate ModSecurity 1.X directives to the 2.0 values. There are also some notes that provide additional information is a directive significantly changed how it operates. +

Feature/CapabilityModSecurity 1.xModSecurity 2.xNotesHow To Upgrade
Apache Version SupportedApache 1.x/2.xApache 2.x OnlyModSecurity 2.0 will only work with Apache 2.x and not the older 1.3 version.If you are mainly an Apache 1.3 shop and/or you have other web servers that you want to protect (such as IIS) an alternative solution is to deploy an Apache 2.x reverse proxy server and implement ModSecurity 2.x on it.
InstallationCan be installed as either a DSO module or as a statically compiled module.Can currently only be installed as a DSO module.In 1.x, you could use apxs directly, while in 2.x you must use the provided Makefile.If you can not use DSOs in your current Apache configs, you may look at implementing a front-end Apache reverse proxy server.
Configuration - IfModuleApache 1.x - <IfModule mod_security.c> + +Apache 2.x - <IfModule security_module><IfModule security2_module>The syntax of using IfModule has changed between Apache 1.x and 2.xMake sure that any existing <IfModule> directives uses the correct syntax.
Processing Phases Supported25ModSecurity 1.x supports:
  • Inbound - which corresponds to current Mod 2.x Request Body phase and the Apache “fixups” phase.

  • Outbound - which corresponds to current Mod 2.x Response Body phase and just after the Apache “response” processing phase.

ModSecurity 2.x supports:
  • Request Headers – which corresponds with the Apache “post-read-request” phase.

  • Request Body – which corresponds with the Apache “fixups” phase.

  • Response Headers – which corresponds to the Apache “response” phase.

  • Response Body – which corresponds to just after the Apache “response” phase.

  • Logging - which is the Apache logging phase.

If you are translating existing 1.x rules (SecFilter/SecFilterSelective) then you should use phase:2 in the new rule syntax. Translate existing OUTPUT rules to run in phase:4.
Directive to turn On/Off the Rule EngineSecFilterEngineSecRuleEngine +ctl:ruleEngine=
  • 1.x – values were On, Off and DynamicOnly and was a Global directive.

  • 2.x - values are On, Off or DetectionOnly.

  • 2.x – the “ctl” action can control the RuleEngine dynamically for individual requests.

Replace SecFilterEngine with SecRuleEngine. The DynamicOnly mode is not supported in ModSecurity 2.x because it was sometimes difficult for ModSecurity to determine if a particular request was dynamic in nature or not. Use of AddType vs. AddHandler would cause problems. Since this logic relies on the internal (and not entirely documented) workings of Apache and on the chosen configuration it also makes it somewhat unpredictable.
Directive to handle the Audit EngineSecAuditEngine On, Off, RelevantOnly, DynamicOrRelevantSecAuditEngine On, Off, RelevantOnlyIn 2.x, the DynamicOrRelevant option was discontinued.If you are using DynamicOrRelevant then switch it to RelevantOnly.
Default Rule ActionSecFilterDefaultActionSecDefaultAction
  • 1.x – SecFilterDefaultAction could be used anywhere in the config and it would be picked up by all rules.

  • 2.x – SecDefaultAction must come before rules and be specified in each context. The default setting for this directive (if it is not specified otherwise) is – +SecDefaultAction phase:2,log,deny,status:403,\

    t:lowercase,t:replaceNulls,\

    t:compressWhitespace

Replace SecFilterDefaultAction with SecDefaultAction. Optionally, you can group rules together where you would like to use the same action and then specify a SecDefaultAction line before each group. + +Also keep in mind that while most actions specified on individual rules will supersede those specified in SecDefaultAction, transformation functions are additive. So, if you specify a “t:base64Decode” transformation function to a rule, it will be added after the lowercase, replaceNulls and compressWhitespace transformation functions.
Debug LoggingSecFilterDebugLog +SecFilterDebugLogLevelSecDebugLog +SecDebugLogLevelName change only.Change the names of these directives to their 2.x counterparts.
Rule Directive(s)SecFilter +SecFilterSelectiveSecRule
  • In Mod 1.x, SecFilter and SecFilterSelective were case insensitive.

  • In Mod 2.x, the case of data is not altered unless the lowercase transformation funce is used. +SecRule has essentially the same rule syntax as SecFilterSelective.

Replace SecFilterSelective with SecRule and make sure to translate the variable tested according to this list. +Replace any SecFilter with a new SecRule directive. You will need to specify a new Variable location and a phase. You can optionally specify a disruptive action, otherwise it will be inherited from a previous SecDefaultAction. +
Rule ExceptionsWhitelist approach – use pass, allow actions +False Positive Approach – use SecFilterRemoveWhitelist approach – use pass, allow and ctl actions. +False Positive Approach – use SecRuleRemoveById and Apache Scope contextIn Mod 2.x, using the “allow” action may not be enough to truly let a request through as “allow” only applies to the current processing phase. This means that rules in subsequent phases may act on the request. This is why you need to also use the “ctl:ruleEngine=Off” action if you really want to let a request through.See Blog post on handling false positives and creating custom rules - http://www.modsecurity.org/blog/archives/2007/02/handling_false.html
Directive to control rule inheritance to Apache Scope locations (Virtual Hosts, Location, Directory)SecFilterInheritanceSecRuleInheritanceThe best use of this directive is when you want to start with a “clean slate” so you can use SecRuleInheritance Off and then specify your new rule sets. +Note – Rule Inheritance does not work across Apache Scope directives (such as Vhosts, Location and Directory directives). This means that you can not use SecRuleInheritance On to inherit a SecDefaultAction directive within these new contexts. This is an issue with the way that Apache inherits contexts. It is for this reason that we recommend that you specify new SecDefaultAction directives within each Apache scope location that you create.Translate any existing “SecFilterInheritance Off” rules directly to “SecRuleInheritance Off”. +Then replace any “SecFilterInheritance On” directives inside Apache Scope context locations with a new SecDefaultAction directive and then import the rules that you want with standard Apache Include directives.
Ability to manage rules in Apache Scope locationsSecFilterImport +SecFilterRemoveSecRuleRemoveById +SecRuleRemoveByMsgSecFilterRemove is now SecRuleRemoveById or SecRuleRemoveByMsg. SecFilterImport is no longer supported.Change all of your existing SecFilterRemove rules to SecRuleRemoveById. For any existing SecFilterImport rules, you will need to either copy the rule into the context or use an Apache Include Directive to include entire files (such as including the Core Rules files).
Ability to verify URL/UTF8 EncodingsSecFilterCheckURLEncoding +SecFilterCheckUnicodeEncoding @validateUrlEncoding +@validateUtf8Encoding In Mod 1.x, these were Global Directives and in Mod 2.x they are Operators that can be applied selectively to each rule.Add the rules that will do exactly the same as the directives
Ability to enforce a Byte Range (allowed character set)SecFilterForceByteRange@validateByteRangeIn Mod 1.x, this was a Global Directive and in Mod 2.x it is an Operator that can be applied selectively to each rule. +In Mod 1.x, this directive did not check POST payloads when multipart/form-data encoding was used.You can now add @validateByteRange operators to individual rules. This helps if you have differences in allowed character sets for different portions of the web application.
Ability to Normalize/Transform Request DataModSecurity 1.x automatically applied the following transformations:
  • On Windows only, convert \ to /

  • Reduce /./ to /

  • Reduce // to /

  • Decode URL-encoded characters

  • Converts Null Bytes to Space character

  • base64Decode

  • base64Encode

  • compressWhitespace

  • escapeSeqDecode

  • hexDecode

  • hexEncode

  • htmlEntityDecode

  • lowercase

  • md5

  • none

  • normalisePath

  • normalisePathWin

  • removeNulls

  • removeWhitespace

  • replaceComments

  • replaceNulls

  • urlDecode

  • urlDecodeUni

  • urlEncode

  • sha1

In Mod 1.x, the normalization functions were implicit and you could not control them. In Mod 2.x, not normalization is done by default. There are now “Transformation Functions” that allow you to selectively apply normalizations and other features.You should add the appropriate transformation functions to either SecDefaultAction directive or each individual rule. See the Core Rules files for examples. +Keep in mind that transformation functions are inherited from parent SecDefaultAction directives. Care should be taken to ensure that RegEx patterns match the data after transformation functions are applied. In order to avoid possible unwanted inherited transformation functions, use “t:none” to either not apply any transformation functions or you can then specify specific transformation functions after “t:none”.
Ability to specify an arbitrary Request Header in a ruleHEADER_headername +HTTP_headernameREQUEST_HEADERS REQUEST_HEADERS:headername +REQUEST_HEADERS:/RegEx/The HTTP_headername syntax has been superseded by the new REQUEST_HEADERS:headername syntax and will not be supported in future releases. The advantage to using the new syntax is that you can also use RegEx in the headername portion.Translate any existing HTTP_headername directives to REQUEST_HEADERS:headername. Also consider consolidating header checks by using Regular Expressions in the header name portion of the Variable.
Variable/Location for the entire URL Request LineTHE_REQUESTREQUEST_LINEFunctions the same. The variable includes the Request Method, URI and HTTP version data.Translate any existing THE_REQUEST directives to REQUEST_LINE directives.
Variable/Location for ArgumentsARG_nameARGS:name +ARGS:/RegEx/Similar to the HTTP_headername situation, the advantage of the new syntax is the ability to use RegEx in the argument name.Translate any existing ARG_name directives to ARGS:name directives.
Accessing Request BodiesSecFilterScanPOST +POST_PAYLOADSecRequestBodyAccess +Phase:2 +REQUEST_BODYIn 2.x, the directive is now called SecRequestBodyAccess and it is more flexible than SecFilterScanPOST as it is able to inspect all request bodies (such as PUT and XML, etc…) and not just POST payloads.Replace the existing SecFilterScanPOST directive with SecRequestBodyAccess. +For individual rules where you want to inspect the request bodies, you must specify REQUEST_BODY as the variable and you also must ensure that it is running in phase:2 (by either an inherited SecDefaultAction setting or by explicitly specifying the phase within the rule action).
Ability to disable POST/Request buffering dynamicallyMODSEC_NOPOSTBUFFERINGctl:requestBodyAccess=OffIn 2.x, you can use the ctl action to turn on/off request body access on a per rule basis.Take any existing entries in the httpd.conf file that set the MODSEC_NOPOSTBUFFERING Env variable and translate them to Mod 2.x rules.
Accessing CookiesCOOKIES +COOKIES_COUNT +COOKIES_NAMES +COOKIES_VALUES +COOKIE_nameREQUEST_HEADERS:Cookie +REQUEST_COOKIES_NAMES REQUEST_COOKIES_NAMES:name +REQUEST_COOKIES_NAMES:/RegEx/ +REQUEST_COOKIES +REQUEST_COOKIES:name REQUEST_COOKIES:/RegEx/ In 2.x, you can use the “&” character to “count” the number of variables. +While there are different ways to access request cookies, the main difference between them are that REQUEST_HEADERS:Cookie will include all of the “raw” Cookie data while any of the REQUEST_COOKIES variable values are parsed.Translate rules as follows – +Mod 1.x -> Mod 2.x +COOKIES -> REQUEST_COOKIES +COOKIES_COUNT -> &REQUEST_COOKIES +COOKIES_NAMES -> REQUEST_COOKIES_NAMES COOKIES_VALUES -> REQUEST_COOKIES +COOKIE_name -> REQUEST_COOKIES:name
Counting VariablesARGS_COUNT +COOKIES_COUNT +HEADERS_COUNT +FILES_COUNT&ARGS +&REQUEST_COOKIES &REQUEST_HEADERS +&FILESIn 2.x, prepending the “&” character will count the number of variables. Example – +1.x – HEADERS_COUNT +2.x - &REQUEST_HEADERSTranslate existing 1.x rules as listed.
Accessing HTTP Status CodeOUTPUT_STATUSRESPONSE_STATUS +Phase:3In 2.x, you need to specify both the RESPONSE_STATUS variable and phase:3 with the rule.Translate any existing 1.x OUTPUT STATUS rules to use RESPONSE_STATUS and phase:3.
Accessing Response Bodies/Post PayloadsSecFilterScanOutput +SecFilterOutputMimeTypes +OUTPUTSecResponseBodyAccess +SecResponseBodyMimeTypes +RESPONSE_BODY +Phase:4In 1.x, neither skipnext nor chain could be used on the OUTPUT location. +In 2.x, both actions can be used on RESPONSE_BODYTranslate directives/rules as follows – +Mod 1.x -> Mod 2.x +SecFilterScanOutput -> SecResponseBodyAccess +SecFilterOutputMimeTypes -> SecResponseBodyMimeTypes +OUTPUT -> RESPONSE_BODY/Phase:4
Cookie NormalizationSecFilterCookieFormat +SecFilterNormalizeCookiesSecCookieFormatSecFilterNormalizeCookies is no longer supported as Mod 2.x transformation functions can now be used to normalize all Variables including Cookie data.Change SecFilterCookieFormat to SecCookieFormat. +When specifying Cookie variables, then apply the applicable transformation functions in the action field of the rule.
Ability to skip rulesskipnextskipIn Mod 2.x – skip takes into account chained rulesets and treats them as 1 rule. +In Mod 1.x – skipnext treated each rule directive as an individual rule regardless of whether or not they were tied together as a chained ruleset.Translate all skipnext rules to skip, however make sure to factor in any chained rulesets that may follow and adjust the skip number accordingly.
Adding/Removing Audit Log Data on a per rule basislogpartsclt:auditLogParts=The rules function the same.Translate any existing logparts actions to the ctl:auditLogParts equivalent.
Inspecting uploaded filesSecUploadApproveScript@inspectFile +FILES_TMPNAMESThe main difference here is that now @inspectFile is an Operator vs. a global Directive. This means that you can apply @inspectFile to individual rules and use different scripts as appropriate. +Also, the return codes are now reversed – +In 1.x, a return code of “1” means that the file would be allowed. +In 2.x, a return code of “1” means that the file would be denied. In order to scan/inspect uploaded files in 2.x, you need to create specific rules that use the FILES_TMPNAMES variable (as these are the names of the files that are temporarily stored on disk) and then use the @inspectFile Operator on each rule. +Also, make sure to swap your return codes in existing scripts as mentioned in the notes column.
Memory limits for uploaded filesSecUploadInMemoryLimitSecRequestBodyInMemoryLimitThese two directives function the same.Change the SecUploadInMemoryLimit directive to SecRequestBodyInMemoryLimit.
diff --git a/2.5.13/2.5.x/doc/migration-matrix.xml b/2.5.13/2.5.x/doc/migration-matrix.xml new file mode 100644 index 00000000..859b3559 --- /dev/null +++ b/2.5.13/2.5.x/doc/migration-matrix.xml @@ -0,0 +1,814 @@ + +
+ ModSecurity Migration Matrix + + + Version 1.0 / (April 10, 2007) + + + 2004-2010 + + Trustwave Holdings, Inc. (http://www.trustwave.com) + + + +
+ Migration from 1.x to 2.x + +
+ If you are already using an older version of ModSecurity and want + to upgrade/migrate your existing custom rules, you will need to ensure + that you properly translate all of your Directives to their + corresponding 2.0 counterparts. Some directives have simply changed + names, however some directives actually behave differently so it is + important that you also review the entire 2.0 Reference Manual. The + migration matrix show below should help you to translate ModSecurity 1.X + directives to the 2.0 values. There are also some notes that provide + additional information is a directive significantly changed how it + operates. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Feature/Capability + + ModSecurity 1.x + + ModSecurity 2.x + + Notes + + How To Upgrade +
+ Apache Version Supported + Apache 1.x/2.xApache 2.x OnlyModSecurity 2.0 will only work with Apache 2.x and not the older + 1.3 version.If you are mainly an Apache 1.3 shop and/or you have other web + servers that you want to protect (such as IIS) an alternative + solution is to deploy an Apache 2.x reverse proxy server and + implement ModSecurity 2.x on it.
+ Installation + Can be installed as either a DSO module or as a statically + compiled module.Can currently only be installed as a DSO module.In 1.x, you could use apxs directly, while in 2.x you must use + the provided Makefile.If you can not use DSOs in your current Apache configs, you may + look at implementing a front-end Apache reverse proxy server.
+ Configuration - IfModule + Apache 1.x - <IfModule mod_security.c> Apache 2.x - + <IfModule security_module><IfModule security2_module>The syntax of using IfModule has changed between Apache 1.x and + 2.xMake sure that any existing <IfModule> directives uses the + correct syntax.
+ Processing Phases Supported + 25ModSecurity 1.x supports: + + Inbound - which corresponds to current Mod 2.x Request + Body phase and the Apache “fixups” phase. + + + + Outbound - which corresponds to current Mod 2.x Response + Body phase and just after the Apache “response” processing + phase. + + ModSecurity 2.x supports: + + Request Headers – which corresponds with the Apache + “post-read-request” phase. + + + + Request Body – which corresponds with the Apache + “fixups” phase. + + + + Response Headers – which corresponds to the Apache + “response” phase. + + + + Response Body – which corresponds to just after the + Apache “response” phase. + + + + Logging - which is the Apache logging phase. + + If you are translating existing 1.x rules + (SecFilter/SecFilterSelective) then you should use phase:2 in the + new rule syntax. Translate existing OUTPUT rules to run in + phase:4.
+ Directive to turn On/Off the Rule + Engine + SecFilterEngineSecRuleEngine ctl:ruleEngine= + + + 1.x – values were On, Off and DynamicOnly and was a + Global directive. + + + + 2.x - values are On, Off or DetectionOnly. + + + + 2.x – the “ctl” action can control the RuleEngine + dynamically for individual requests. + + + + + Replace SecFilterEngine with SecRuleEngine. The DynamicOnly mode + is not supported in ModSecurity 2.x because it was sometimes + difficult for ModSecurity to determine if a particular request was + dynamic in nature or not. Use of AddType vs. AddHandler would cause + problems. Since this logic relies on the internal (and not entirely + documented) workings of Apache and on the chosen configuration it + also makes it somewhat unpredictable.
+ Directive to handle the Audit + Engine + SecAuditEngine On, Off, RelevantOnly, DynamicOrRelevantSecAuditEngine On, Off, RelevantOnlyIn 2.x, the DynamicOrRelevant option was discontinued.If you are using DynamicOrRelevant then switch it to + RelevantOnly.
+ Default Rule Action + SecFilterDefaultActionSecDefaultAction + + + 1.x – SecFilterDefaultAction could be used anywhere in + the config and it would be picked up by all rules. + + + + 2.x – SecDefaultAction must come before rules and be + specified in each context. The default setting for this + directive (if it is not specified otherwise) is – + SecDefaultAction + phase:2,log,deny,status:403,\ + + + t:lowercase,t:replaceNulls,\ + + + + t:compressWhitespace + + + + + + Replace SecFilterDefaultAction with SecDefaultAction. + Optionally, you can group rules together where you would like to use + the same action and then specify a SecDefaultAction line before each + group. Also keep in mind that while most actions specified on + individual rules will supersede those specified in SecDefaultAction, + transformation functions are additive. So, if you specify a + “t:base64Decode” transformation function to a rule, it will be added + after the lowercase, replaceNulls and compressWhitespace + transformation functions.
+ Debug Logging + SecFilterDebugLog SecFilterDebugLogLevelSecDebugLog SecDebugLogLevelName change only.Change the names of these directives to their 2.x + counterparts.
+ Rule Directive(s) + SecFilter SecFilterSelectiveSecRule + + + In Mod 1.x, SecFilter and SecFilterSelective were case + insensitive. + + + + In Mod 2.x, the case of data is not altered unless the + lowercase transformation funce is used. SecRule has + essentially the same rule syntax as SecFilterSelective. + + + Replace SecFilterSelective with SecRule and make sure to + translate the variable tested according to this list. Replace any + SecFilter with a new SecRule directive. You will need to specify a + new Variable location and a phase. You can optionally specify a + disruptive action, otherwise it will be inherited from a previous + SecDefaultAction.
+ Rule Exceptions + Whitelist approach – use pass, allow actions False Positive + Approach – use SecFilterRemoveWhitelist approach – use pass, allow and ctl actions. False + Positive Approach – use SecRuleRemoveById and Apache Scope + contextIn Mod 2.x, using the “allow” action may not be enough to truly + let a request through as “allow” only applies to the current + processing phase. This means that rules in subsequent phases may act + on the request. This is why you need to also use the + “ctl:ruleEngine=Off” action if you really want to let a request + through.See Blog post on handling false positives and creating custom + rules - + http://www.modsecurity.org/blog/archives/2007/02/handling_false.html
+ Directive to control rule inheritance to + Apache Scope locations (Virtual Hosts, Location, + Directory) + SecFilterInheritanceSecRuleInheritanceThe best use of this directive is when you want to start with a + “clean slate” so you can use SecRuleInheritance Off and then specify + your new rule sets. Note – Rule Inheritance does not work across + Apache Scope directives (such as Vhosts, Location and Directory + directives). This means that you can not use SecRuleInheritance On + to inherit a SecDefaultAction directive within these new contexts. + This is an issue with the way that Apache inherits contexts. It is + for this reason that we recommend that you specify new + SecDefaultAction directives within each Apache scope location that + you create.Translate any existing “SecFilterInheritance Off” rules directly + to “SecRuleInheritance Off”. Then replace any “SecFilterInheritance + On” directives inside Apache Scope context locations with a new + SecDefaultAction directive and then import the rules that you want + with standard Apache Include directives.
+ Ability to manage rules in Apache Scope + locations + SecFilterImport SecFilterRemoveSecRuleRemoveById SecRuleRemoveByMsgSecFilterRemove is now SecRuleRemoveById or SecRuleRemoveByMsg. + SecFilterImport is no longer supported.Change all of your existing SecFilterRemove rules to + SecRuleRemoveById. For any existing SecFilterImport rules, you will + need to either copy the rule into the context or use an Apache + Include Directive to include entire files (such as including the + Core Rules files).
+ Ability to verify URL/UTF8 + Encodings + SecFilterCheckURLEncoding SecFilterCheckUnicodeEncoding @validateUrlEncoding @validateUtf8Encoding In Mod 1.x, these were Global Directives and in Mod 2.x they are + Operators that can be applied selectively to each rule.Add the rules that will do exactly the same as the + directives
+ Ability to enforce a Byte Range (allowed + character set) + SecFilterForceByteRange@validateByteRangeIn Mod 1.x, this was a Global Directive and in Mod 2.x it is an + Operator that can be applied selectively to each rule. In Mod 1.x, + this directive did not check POST payloads when multipart/form-data + encoding was used.You can now add @validateByteRange operators to individual + rules. This helps if you have differences in allowed character sets + for different portions of the web application.
+ Ability to Normalize/Transform Request + Data + ModSecurity 1.x automatically applied the following + transformations: + + On Windows only, convert \ to / + + + + Reduce /./ to / + + + + Reduce // to / + + + + Decode URL-encoded characters + + + + Converts Null Bytes to Space character + + + + + base64Decode + + + + base64Encode + + + + compressWhitespace + + + + escapeSeqDecode + + + + hexDecode + + + + hexEncode + + + + htmlEntityDecode + + + + lowercase + + + + md5 + + + + none + + + + normalisePath + + + + normalisePathWin + + + + removeNulls + + + + removeWhitespace + + + + replaceComments + + + + replaceNulls + + + + urlDecode + + + + urlDecodeUni + + + + urlEncode + + + + sha1 + + + + + In Mod 1.x, the normalization functions were implicit and you + could not control them. In Mod 2.x, not normalization is done by + default. There are now “Transformation Functions” that allow you to + selectively apply normalizations and other features.You should add the appropriate transformation functions to + either SecDefaultAction directive or each individual rule. See the + Core Rules files for examples. Keep in mind that transformation + functions are inherited from parent SecDefaultAction directives. + Care should be taken to ensure that RegEx patterns match the data + after transformation functions are applied. In order to avoid + possible unwanted inherited transformation functions, use “t:none” + to either not apply any transformation functions or you can then + specify specific transformation functions after “t:none”.
+ Ability to specify an arbitrary Request + Header in a rule + HEADER_headername HTTP_headernameREQUEST_HEADERS REQUEST_HEADERS:headername + REQUEST_HEADERS:/RegEx/The HTTP_headername syntax has been superseded by the new + REQUEST_HEADERS:headername syntax and will not be supported in + future releases. The advantage to using the new syntax is that you + can also use RegEx in the headername portion.Translate any existing HTTP_headername directives to + REQUEST_HEADERS:headername. Also consider consolidating header + checks by using Regular Expressions in the header name portion of + the Variable.
+ Variable/Location for the entire URL Request + Line + THE_REQUESTREQUEST_LINEFunctions the same. The variable includes the Request Method, + URI and HTTP version data.Translate any existing THE_REQUEST directives to REQUEST_LINE + directives.
+ Variable/Location for Arguments + ARG_nameARGS:name ARGS:/RegEx/Similar to the HTTP_headername situation, the advantage of the + new syntax is the ability to use RegEx in the argument name.Translate any existing ARG_name directives to ARGS:name + directives.
+ Accessing Request Bodies + SecFilterScanPOST POST_PAYLOADSecRequestBodyAccess Phase:2 REQUEST_BODYIn 2.x, the directive is now called SecRequestBodyAccess and it + is more flexible than SecFilterScanPOST as it is able to inspect all + request bodies (such as PUT and XML, etc…) and not just POST + payloads.Replace the existing SecFilterScanPOST directive with + SecRequestBodyAccess. For individual rules where you want to inspect + the request bodies, you must specify REQUEST_BODY as the variable + and you also must ensure that it is running in phase:2 (by either an + inherited SecDefaultAction setting or by explicitly specifying the + phase within the rule action).
+ Ability to disable POST/Request buffering + dynamically + MODSEC_NOPOSTBUFFERINGctl:requestBodyAccess=OffIn 2.x, you can use the ctl action to turn on/off request body + access on a per rule basis.Take any existing entries in the httpd.conf file that set the + MODSEC_NOPOSTBUFFERING Env variable and translate them to Mod 2.x + rules.
+ Accessing Cookies + COOKIES COOKIES_COUNT COOKIES_NAMES COOKIES_VALUES + COOKIE_nameREQUEST_HEADERS:Cookie REQUEST_COOKIES_NAMES + REQUEST_COOKIES_NAMES:name REQUEST_COOKIES_NAMES:/RegEx/ + REQUEST_COOKIES REQUEST_COOKIES:name REQUEST_COOKIES:/RegEx/ In 2.x, you can use the “&” character to “count” the number + of variables. While there are different ways to access request + cookies, the main difference between them are that + REQUEST_HEADERS:Cookie will include all of the “raw” Cookie data + while any of the REQUEST_COOKIES variable values are parsed.Translate rules as follows – Mod 1.x -> Mod 2.x COOKIES -> + REQUEST_COOKIES COOKIES_COUNT -> &REQUEST_COOKIES + COOKIES_NAMES -> REQUEST_COOKIES_NAMES COOKIES_VALUES -> + REQUEST_COOKIES COOKIE_name -> REQUEST_COOKIES:name
+ Counting Variables + ARGS_COUNT COOKIES_COUNT HEADERS_COUNT FILES_COUNT&ARGS &REQUEST_COOKIES &REQUEST_HEADERS + &FILESIn 2.x, prepending the “&” character will count the number + of variables. Example – 1.x – HEADERS_COUNT 2.x - + &REQUEST_HEADERSTranslate existing 1.x rules as listed.
+ Accessing HTTP Status Code + OUTPUT_STATUSRESPONSE_STATUS Phase:3In 2.x, you need to specify both the RESPONSE_STATUS variable + and phase:3 with the rule.Translate any existing 1.x OUTPUT STATUS rules to use + RESPONSE_STATUS and phase:3.
+ Accessing Response Bodies/Post + Payloads + SecFilterScanOutput SecFilterOutputMimeTypes OUTPUTSecResponseBodyAccess SecResponseBodyMimeTypes RESPONSE_BODY + Phase:4In 1.x, neither skipnext nor chain could be used on the OUTPUT + location. In 2.x, both actions can be used on RESPONSE_BODYTranslate directives/rules as follows – Mod 1.x -> Mod 2.x + SecFilterScanOutput -> SecResponseBodyAccess + SecFilterOutputMimeTypes -> SecResponseBodyMimeTypes OUTPUT -> + RESPONSE_BODY/Phase:4
+ Cookie Normalization + SecFilterCookieFormat SecFilterNormalizeCookiesSecCookieFormatSecFilterNormalizeCookies is no longer supported as Mod 2.x + transformation functions can now be used to normalize all Variables + including Cookie data.Change SecFilterCookieFormat to SecCookieFormat. When specifying + Cookie variables, then apply the applicable transformation functions + in the action field of the rule.
+ Ability to skip rules + skipnextskipIn Mod 2.x – skip takes into account chained rulesets and treats + them as 1 rule. In Mod 1.x – skipnext treated each rule directive as + an individual rule regardless of whether or not they were tied + together as a chained ruleset.Translate all skipnext rules to skip, however make sure to + factor in any chained rulesets that may follow and adjust the skip + number accordingly.
+ Adding/Removing Audit Log Data on a per rule + basis + logpartsclt:auditLogParts=The rules function the same.Translate any existing logparts actions to the ctl:auditLogParts + equivalent.
+ Inspecting uploaded files + SecUploadApproveScript@inspectFile FILES_TMPNAMESThe main difference here is that now @inspectFile is an Operator + vs. a global Directive. This means that you can apply @inspectFile + to individual rules and use different scripts as appropriate. Also, + the return codes are now reversed – In 1.x, a return code of “1” + means that the file would be allowed. In 2.x, a return code of “1” + means that the file would be denied. In order to scan/inspect uploaded files in 2.x, you need to + create specific rules that use the FILES_TMPNAMES variable (as these + are the names of the files that are temporarily stored on disk) and + then use the @inspectFile Operator on each rule. Also, make sure to + swap your return codes in existing scripts as mentioned in the notes + column.
+ Memory limits for uploaded files + SecUploadInMemoryLimitSecRequestBodyInMemoryLimitThese two directives function the same.Change the SecUploadInMemoryLimit directive to + SecRequestBodyInMemoryLimit.
+
+
+
diff --git a/2.5.13/2.5.x/doc/modsecurity-logo.png b/2.5.13/2.5.x/doc/modsecurity-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7fa98c5a3723a4363e1f258c516232f0bac7331b GIT binary patch literal 100823 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

>}ofgy8v0iF13@QK~9x_+MCw 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

FD6}$iU!?hP0!G z>Eo{r5~C*Y>(vz%*)rZvx~^NR-%;%hz?;#n=7mshKwI!PX&x6MyeZ31BQONAoWl)v(9>o)QacxU-5Ec4aF@aBRv^C#aR?w^HVn7v6&YuWccX}QfSN5 zUsh)Gm;do7@?DgB#f>MvD+f(N+B?q(QE}1(LPg?RjOzrxS3;-8b(|9NxRh2B7W{I_ zIDAf1znYE4f4D<#j=Sa3t^1h7$}bYOr(DSzIP(nZ({N+3s+uw~Dq{WbH}T)4y~j%A z72?i6rcpRJJ7-yq38v36g0B5NO0~h(;#2SPjTi44gsiaj2{xu8kt%gxsqVW;Hy(t= zllxD&zy$8?i!6hwBZ-Je7b{$IV8{Yh+6U7+Vfv6sRJ(X2_J*YUtK@rO1#ZkZcVC|LkVM&H8LE* z2;cc$YS(kINVZmbTJZs7YcK7CDlxBmr40`7d)4SsL9ojPo62QMh@BX}P z&3D=4BY{^KU2si0C=b}>udio2ZrB3YokDV4E+;2v^6@!a4`|GQs%z?SK>C8g{4I?0 z25Z=kLOF_TaiTilf472U^wjZ z%@Xv|(n4BN(j!B|;a|Ug{rvOz!2_5zdz@@IxWgtd^f7|3*9ul}=keqC@YHZzLeL%u zF?pc??AH9;(qqom;n@r2npM-S^4~Y62=@uf#4zhieWO4)3`8GaU=t|fd~aa|$e3mF z#B5otk;EjXCMIAInwU5;BJ}GcD=TT~atJg%=rYxXnD+l3pe$tdpO+XxwIli;{Z`5U hp2Gb9*}2q21h(TVPk3g2M-YI&dw2Ejl-z=a{U2sFX0HGM literal 0 HcmV?d00001 diff --git a/2.5.13/2.5.x/doc/modsecurity-reference.css b/2.5.13/2.5.x/doc/modsecurity-reference.css new file mode 100644 index 00000000..1c89c88d --- /dev/null +++ b/2.5.13/2.5.x/doc/modsecurity-reference.css @@ -0,0 +1,102 @@ + +body { + font: 13px/20px Arial, Helvetica; + background-color: white; + width: 800px; +} + +/* TEXT */ + +PRE { + font-size: 100%; + padding: 5px; + border-style: solid; + border-width: 1px; + border-color: #CCCCCC; + background-color: #F5F8FA; +} + +p, td, tr, li, ol, ul { + /*font-family: Trebuchet MS, Verdana, Tahoma, Arial, Helvetica, Geneva;*/ + /*font-family: Arial, Helvetica; + font-size: 14px; + line-height: 24px;*/ +} + +.copyright { + font-size: 10px; +} + +h1 { + padding-top: 40px; + font: 24px/30px Arial, Helvetica; + font-weight: bold; +} + +h2 { + font: 20px/30px Arial, Helvetica; + font-weight: bold; +} + +h3 { + padding-top: 15px; + font: 16px/10px Arial, Helvetica; + font-weight: bold; +} + +h4 { + padding-top: 15px; + font: 14px/10px Arial, Helvetica; + font-weight: bold; +} + + + + +.header { + background-color: #00448B; + border-top: 6px solid #002B6E; + height: 84px; + vertical-align: top; + padding-left: 20px; + padding-top: 10px; +} + +.topNavigation { + background-color: #EEEEEE; + background: url('g/topnav-background.gif'); + height: 23px; + line-height: 12px; + vertical-align: top; + padding-left: 12px; + padding-top: 5px; +} + +.topLink, A.topLink:link, A.topLink:active, A.topLink:visited { + font-weight: bold; + color: black; + text-decoration: none; + padding-left: 8px; + padding-right: 8px; +} + +A.topLink:hover { + color: #BB0000; +} + +#navheader td { + font-size: 12px; +} + +#navfooter td { + font-size: 12px; +} + +h3.title { + margin-top: 0px; +} + +.note { + border-top: 1px solid #CCCCCC; + border-bottom: 1px solid #CCCCCC; +} diff --git a/2.5.13/2.5.x/doc/modsecurity.gif b/2.5.13/2.5.x/doc/modsecurity.gif new file mode 100644 index 0000000000000000000000000000000000000000..76f5ca1632677cb890e0a5ad57cf47cb8a4a6c8d GIT binary patch literal 2585 zcmV+!3g-1kNk%w1VR!%}0OkMyMS8T;+UNWG{a1IVdy~1WyVD0Zeqx@~4m^P!N{eWu z+9FMj7(|8^LWB}Nfh<&!G+UL8qQhB|$|qKpQi;NvtjcVu-t+bNJ#npVhq6Rxo$c`T zLUys-;_OO*x;kyDV4KktK!Rze+>)imRgK2z?DH^Tp62TExy0F|w9hJ9nIlkm!FW4o`jp2nVgKB zq^Y5Xm8+(xjgO_8v$Kh)u)Ds!qmsX!s<67EtCPvKoXpL&uf)R3$-S!6vX{oYmAI#* z&bZvK!l91m;Mvx|+RCY{*wxL%^V;^HuJyjove%iC0NcSUc8j1fWRs8y)Fn?|EL+tW z4ntRt*D!AL^tB3x1c66$qb#ngn5&qk227Z6`E*ekvt!2^vTS&z$%qjI^!bBn5Z|;- z45V=2aAe9Elk4cUqIK?8J5w7SAt=zN&z?V1o<4e6GS|$dhZ?l7V1WXcK~oxJZF-L& zz@7g8;{HOAqG|xF0zF+z2ba^)jDH$7HbWKmr3Zf}n#4JaCv9CkUjWWd{Wapd}0_XnBVLwf5n}4pdZyf&l|)nZplS_OJpA zI)DLy76+7}02FG(p@0}lz%fG^B4FSE8ZU$a1_cy=A%+fJ*l++CWI*8n6k&Lp2OK)Q z0Kym|P(a5QUwF`k9tU`;M;tGRF@qOOupx%PUX+-PDXmsD1Ox_9)rS>+AmC;HA3H#x z>5;^42r&j22l&9i1Uv|_0tO0@K|u%(*zkfG2joEk z8473-0uvb6@d5=CoQw3jOE(Qh3@SkGLDeC6k#!g*gh0U;I(&ct2Sb0r#80fcsVGyWC~JF93lH*jZiK_?N@~v0)BBTL7|1@P{*m1QZHn;M*W5K`T}80diQL207?K zophCkSl2@Zgt4oe3w9|BQ-L&VzA zcz_r^d_fN;c!2`o&<2orBZ|RuAQk6U1iNAJ0T-ys04h)wEYL>(O~&y<0unFGMj7EqvKT2Me3D6oa@MZpgakbxKu z0D={?U<^6{0OUB(Ckdcomoh*A6v)6seEm?E`zvO;LePdII4uTdh=C1oV34LrbBe^P z$_^Hg$vzNa3Y^px7b@9@Av^&BRX7m5J4Ht0D>@#Aq+fdT@)0sh8V;k3cFgC5GDNExmM8iPPU21-zZ008d> zti?hD`$hj>cRl zsK&T_h{3^PKmiqS0S7YB!@)@KfpX3OjUWI49L~Z80Hn7DHS|IYK;YOFgaBURLj!c4 z0S)N7;JRz*v#P0yOXh4J-fwC6dGc1D>#iEck)IeozAgR-ghlCUw1InS3@}1?F@-E- zfub`svUP5QgDhC*5I8^rZYj!%Ea3JGMaIDa?bsa>se*=F0HP$m)14(V0?bE24wJBq z97M3WL;)rhauOh39e6-hjmZZBF~SR|xMLX<`3GR=F%f9``67Pc!UQ~}0Nsg1BogUM zL}t>HL1!c&0oeyMD9wpBe82+goWM$uLLWW@de621OEd_*7nZDg9GGznBN%W500003 zo6g5V9s-PhoI=pO21j1d5 zX!p4s=Gcr&{EX?<{6jK)dC0c6{f>>eWHOItb-0yM>~3GD-K-|aUm!y4K6{%X--tCx vo + +

+ <trademark class="registered">ModSecurity</trademark> Reference + Manual + + + Version 2.5.13-dev1 (Feb 5, 2010) + + + 2004-2010 + + Trustwave Holdings, Inc. (http://www.trustwave.com) + + + +
+ Introduction + + ModSecurity is a web application firewall (WAF). With over 70% of + attacks now carried out over the web application level, organisations need + all the help they can get in making their systems secure. WAFs are + deployed to establish an increased external security layer to detect + and/or prevent attacks before they reach web applications. ModSecurity + provides protection from a range of attacks against web applications and + allows for HTTP traffic monitoring and real-time analysis with little or + no changes to existing infrastructure. + +
+ HTTP Traffic Logging + + Web servers are typically well-equipped to log traffic in a form + useful for marketing analyses, but fall short logging traffic to web + applications. In particular, most are not capable of logging the request + bodies. Your adversaries know this, and that is why most attacks are now + carried out via POST requests, rendering your systems blind. ModSecurity + makes full HTTP transaction logging possible, allowing complete requests + and responses to be logged. Its logging facilities also allow + fine-grained decisions to be made about exactly what is logged and when, + ensuring only the relevant data is recorded. As some of the request + and/or response may contain sensitive data in certain fields, + ModSecurity can be configured to mask these fields before they are + written to the audit log. +
+ +
+ Real-Time Monitoring and Attack Detection + + In addition to providing logging facilities, ModSecurity can + monitor the HTTP traffic in real time in order to detect attacks. In + this case, ModSecurity operates as a web intrusion detection tool, + allowing you to react to suspicious events that take place at your web + systems. +
+ +
+ Attack Prevention and Just-in-time Patching + + ModSecurity can also act immediately to prevent attacks from + reaching your web applications. There are three commonly used + approaches: + + + + Negative security model. A negative security model monitors + requests for anomalies, unusual behaviour, and common web + application attacks. It keeps anomaly scores for each request, IP + addresses, application sessions, and user accounts. Requests with + high anomaly scores are either logged or rejected altogether. + + + + Positive security model. When a positive security model is + deployed, only requests that are known to be valid are accepted, + with everything else rejected. This model requires knownledge of the + web applications you are protecting. Therefore a positive security + model works best with applications that are heavily used but rarely + updated so that maintenance of the model is minimized. + + + + Known weaknesses and vulnerabilities. Its rule language makes + ModSecurity an ideal external patching tool. External patching + (sometimes referred to as Virtual Patching) is about reducing the + window of opportunity. Time needed to patch application + vulnerabilities often runs to weeks in many organisations. With + ModSecurity, applications can be patched from the outside, without + touching the application source code (and even without any access to + it), making your systems secure until a proper patch is applied to + the application. + + +
+ +
+ Flexible Rule Engine + + A flexible rule engine sits in the heart of ModSecurity. It + implements the ModSecurity Rule Language, which is a specialised + programming language designed to work with HTTP transaction data. The + ModSecurity Rule Language is designed to be easy to use, yet flexible: + common operations are simple while complex operations are possible. + Certified ModSecurity Rules, included with ModSecurity, contain a + comprehensive set of rules that implement general-purpose hardening, + protocol validation and detection of common web application security + issues. Heavily commented, these rules can be used as a learning + tool. +
+ +
+ Embedded-mode Deployment + + ModSecurity is an embeddable web application firewall, which means + it can be deployed as part of your existing web server infrastructure + provided your web servers are Apache-based. This deployment method has + certain advantages: + + + + No changes to existing network. It only takes a few minutes to + add ModSecurity to your existing web servers. And because it was + designed to be completely passive by default, you are free to deploy + it incrementally and only use the features you need. It is equally + easy to remove or deactivate it if required. + + + + No single point of failure. Unlike with network-based + deployments, you will not be introducing a new point of failure to + your system. + + + + Implicit load balancing and scaling. Because it works embedded + in web servers, ModSecurity will automatically take advantage of the + additional load balancing and scalability features. You will not + need to think of load balancing and scaling unless your existing + system needs them. + + + + Minimal overhead. Because it works from inside the web server + process there is no overhead for network communication and minimal + overhead in parsing and data exchange. + + + + No problem with encrypted or compressed content. Many IDS + systems have difficulties analysing SSL traffic. This is not a + problem for ModSecurity because it is positioned to work when the + traffic is decrypted and decompressed. + + +
+ +
+ Network-based Deployment + + ModSecurity works equally well when deployed as part of an + Apache-based reverse proxy server, and many of our customers choose to + do so. In this scenario, one installation of ModSecurity can protect any + number of web servers (even the non-Apache ones). +
+ +
+ Portability + + ModSecurity is known to work well on a wide range of operating + systems. Our customers are successfully running it on Linux, Windows, + Solaris, FreeBSD, OpenBSD, NetBSD, AIX, Mac OS X, and HP-UX. +
+ +
+ Licensing + + ModSecurity is available under two licenses. Users can choose to + use the software under the terms of the GNU General Public License + version 2 (licence text is included with the distribution), as an Open + Source / Free Software product. A range of commercial licenses is also + available, together with a range of commercial support contracts. For + more information on commercial licensing please contact Trustwave Holdings + Security. + + + ModSecurity, mod_security, ModSecurity Pro, and ModSecurity Core + Rules are trademarks or registered trademarks of Trustwave Holdings, + Inc. + +
+
+ +
+ <trademark>ModSecurity Core Rules</trademark> + +
+ Overview + + ModSecurity is a web application firewall engine that provides + very little protection on its own. In order to become useful, + ModSecurity must be configured with rules. In order to enable users to + take full advantage of ModSecurity out of the box, Trustwave Holdings, Inc. + is providing a free certified rule set for ModSecurity 2.x. Unlike + intrusion detection and prevention systems, which rely on signatures + specific to known vulnerabilities, the Core Rules provide generic + protection from unknown vulnerabilities often found in web applications, + which are in most cases custom coded. The Core Rules are heavily + commented to allow it to be used as a step-by-step deployment guide for + ModSecurity. The latest Core Rules can be found at the ModSecurity + website - http://www.modsecurity.org/projects/rules/. +
+ +
+ Core Rules Content + + In order to provide generic web applications protection, the Core + Rules use the following techniques: + + + + HTTP protection - detecting violations of the HTTP protocol + and a locally defined usage policy. + + + + Common Web Attacks Protection - detecting common web + application security attack. + + + + Automation detection - Detecting bots, crawlers, scanners and + other surface malicious activity. + + + + Trojan Protection - Detecting access to Trojans horses. + + + + Error Hiding - Disguising error messages sent by the + server. + + +
+
+ +
+ Installation + + ModSecurity installation requirements: + + + + ModSecurity 2.x works only with Apache 2.0.x or higher. Version + 2.2.x is highly recommended. + + + + Make sure you have mod_unique_id installed. + + mod_unique_id is packaged with Apache httpd. + + + + libapr and libapr-util + + http://apr.apache.org/ + + + + libpcre + + http://www.pcre.org/ + + + + libxml2 + + http://xmlsoft.org/downloads.html + + + + liblua v5.1.x + + This library is optional and only needed if you will be using + the new Lua engine. + + http://www.lua.org/download.html + + Note that ModSecurity requires the dynamic libraries. These are + not built by default in the source distribution, so the binary + distribution is recommended. + + + + libcurl v7.15.1 or higher + + If you will be using the ModSecurity Log Collector (mlogc) to + send audit logs to a central repository, then you will also need the + curl library. + + http://curl.haxx.se/libcurl/ + + + Many have had issues with libcurl linked with the GnuTLS + library for SSL/TLS support. It is recommended that the openssl + library be used for SSL/TLS support in libcurl. + + + + + ModSecurity installation consists of the following steps: + + + + Stop Apache httpd + + + + Unpack the ModSecurity archive + + + + Building differs for UNIX (or UNIX-like) operating systems and + Windows. + + + + UNIX + + + + Run the configure script to generate a Makefile. + Typically no options are needed. + + ./configure + + Options are available for more customization (use + ./configure --help for a full list), but + typically you will only need to specify the location of the + apxs command installed by Apache httpd with + the --with-apxs option. + + ./configure + --with-apxs=/path/to/httpd-2.x.y/bin/apxs + + + There are certain configure options that are meant for + debugging an other development use. If enabled, these + options can substantially impact performance. These options + include all --debug-* options as well as + the --enable-performance-measurements + options. + + + + + Compile with: make + + + + Optionally test with: make + test + + + This is step is still a bit experimental. If you have + problems, please send the full output and error from the + build to the support list. Most common issues are related to + not finding the required headers and/or libraries. + + + + + Optionally build the ModSecurity Log Collector with: + make mlogc + + + + Optionally install mlogc: Review the + INSTALL file included in the + apache2/mlogc-src directory in the distribution. + + + + Install the ModSecurity module with: make + install + + + + + + Windows (MS VC++ 8) + + + + Edit Makefile.win to configure the + Apache base and library paths. + + + + Compile with: nmake -f + Makefile.win + + + + Install the ModSecurity module with: nmake -f + Makefile.win install + + + + Copy the libxml2.dll and + lua5.1.dll to the Apache + bin directory. Alternatively you can follow + the step below for using LoadFile to load these + libraries. + + + + + + + + Edit the main Apache httpd config file (usually + httpd.conf) + + On UNIX (and Windows if you did not copy the DLLs as stated + above) you must load libxml2 and lua5.1 before ModSecurity with + something like this: + + LoadFile /usr/lib/libxml2.so +LoadFile /usr/lib/liblua5.1.so + + Load the ModSecurity module with:LoadModule security2_module modules/mod_security2.so + + + + Configure ModSecurity + + + + Start Apache httpd + + + + You should now have ModSecurity 2.x up and running. + + + + + If you have compiled Apache yourself you might experience problems + compiling ModSecurity against PCRE. This is because Apache bundles PCRE + but this library is also typically provided by the operating system. I + would expect most (all) vendor-packaged Apache distributions to be + configured to use an external PCRE library (so this should not be a + problem). + + You want to avoid Apache using the bundled PCRE library and + ModSecurity linking against the one provided by the operating system. + The easiest way to do this is to compile Apache against the PCRE library + provided by the operating system (or you can compile it against the + latest PCRE version you downloaded from the main PCRE distribution + site). You can do this at configure time using the --with-pcre switch. If you are not in a + position to recompile Apache, then, to compile ModSecurity successfully, + you'd still need to have access to the bundled PCRE headers (they are + available only in the Apache source code) and change the include path + for ModSecurity (as you did in step 7 above) to point to them (via the + --with-pcre ModSecurity configure option). + + Do note that if your Apache is using an external PCRE library you + can compile ModSecurity with WITH_PCRE_STUDY defined,which would possibly + give you a slight performance edge in regular expression + processing. + + Non-gcc compilers may have problems running out-of-the-box as the + current build system was designed around the gcc compiler and some + compiler/linker flags may differ. To use a non-gcc compiler you may need + some manual Makefile tweaks if issues cannot be solved by exporting + custom CFLAGS and CPPFLAGS environment variables. + + If you are upgrading from ModSecurity 1.x, please refer to the + migration matrix at http://www.modsecurity.org/documentation/ModSecurity-Migration-Matrix.pdf + +
+ +
+ Configuration Directives + + The following section outlines all of the ModSecurity directives. + Most of the ModSecurity directives can be used inside the various Apache + Scope Directives such as VirtualHost, + Location, LocationMatch, + Directory, etc... There are others, however, that can + only be used once in the main configuration file. This information is + specified in the Scope sections below. The first version to use a given + directive is given in the Version sections below. + + These rules, along with the Core rules files, should be contained is + files outside of the httpd.conf file and called up with Apache "Include" + directives. This allows for easier updating/migration of the rules. If you + create your own custom rules that you would like to use with the Core + rules, you should create a file called - + modsecurity_crs_15_customrules.conf and place it in + the same directory as the Core rules files. By using this file name, your + custom rules will be called up after the standard ModSecurity Core rules + configuration file but before the other Core rules. This allows your rules + to be evaluated first which can be useful if you need to implement + specific "allow" rules or to correct any false positives in the Core rules + as they are applied to your site. + + + It is highly encouraged that you do not edit the Core rules files + themselves but rather place all changes (such as + SecRuleRemoveByID, etc...) in your custom rules file. + This will allow for easier upgrading as newer Core rules are released by + Trustwave Holdings on the ModSecurity website. + + +
+ <literal>SecAction</literal> + + Description: Unconditionally processes the + action list it receives as the first and only parameter. It accepts one + parameter, the syntax of which is identical to the third parameter + of SecRule. + + Syntax: SecAction + action1,action2,action3 + + Example Usage: SecAction + nolog,phase:1,initcol:RESOURCE=%{REQUEST_FILENAME} + + Processing Phase: Any + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: None + + SecAction is best used when you unconditionally execute an action. + This is explicit triggering whereas the normal Actions are conditional + based on data inspection of the request/response. This is a useful + directive when you want to run certain actions such as + initcol to initialize collections. +
+ +
+ <literal>SecArgumentSeparator</literal> + + Description: Specifies which character to use + as separator for + application/x-www-form-urlencoded content. Defaults to + &. Applications are sometimes + (very rarely) written to use a semicolon (;). + + Syntax: SecArgumentSeparator character + + Example Usage: SecArgumentSeparator ; + + Processing Phase: Any + + Scope: Main + + Version: 2.0.0 + + Dependencies/Notes: None + + This directive is needed if a backend web application is using a + non-standard argument separator. If this directive is not set properly + for each web application, then ModSecurity will not be able to parse the + arguments appropriately and the effectiveness of the rule matching will + be significantly decreased. +
+ +
+ <literal>SecAuditEngine</literal> + + Description: Configures the audit logging + engine. + + Syntax: SecAuditEngine On|Off|RelevantOnly + + Example Usage: SecAuditEngine On + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: Can be set/changed with + the "ctl" action for the current transaction. + + Example: The following example shows the various audit directives + used together. + + SecAuditEngine RelevantOnly +SecAuditLog logs/audit/audit.log +SecAuditLogParts ABCFHZ +SecAuditLogType concurrent +SecAuditLogStorageDir logs/audit +SecAuditLogRelevantStatus ^(?:5|4\d[^4]) + + Possible values are: + + + + On - log all transactions + by default. + + + + Off - do not log + transactions by default. + + + + RelevantOnly - by default + only log transactions that have triggered a warning or an error, or + have a status code that is considered to be relevant (see SecAuditLogRelevantStatus). + + +
+ +
+ <literal>SecAuditLog</literal> + + Description: Defines the path to the main + audit log file. + + Syntax: SecAuditLog + /path/to/auditlog + + Example Usage: SecAuditLog + /usr/local/apache/logs/audit.log + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: This file is open on + startup when the server typically still runs as + root. You should not allow non-root users to have write + privileges for this file or for the directory it is stored in.. + + This file will be used to store the audit log entries if serial + audit logging format is used. If concurrent audit logging format is used + this file will be used as an index, and contain a record of all audit + log files created. If you are planning to use Concurrent audit logging + and sending your audit log data off to a remote Console host or + commercial ModSecurity Management Appliance, then you will need to + configure and use the ModSecurity Log Collector (mlogc) and use the + following format for the audit log: + + + Prior to 2.5.13 you may have been able to use a relative path to + a piped logger command, but this was broken on some platforms and the + feature was removed in 2.5.13 to make the platforms more + consitent. + + + SecAuditLog "|/path/to/mlogc /path/to/mlogc.conf" +
+ +
+ <literal>SecAuditLog2</literal> + + Description: Defines the path to the + secondary audit log index file when concurrent logging is enabled. See + SecAuditLog2 for more details. + + Syntax: SecAuditLog2 + /path/to/auditlog2 + + Example Usage: SecAuditLog2 + /usr/local/apache/logs/audit2.log + + Processing Phase: N/A + + Scope: Any + + Version: 2.1.2 + + Dependencies/Notes: A main audit log must be + defined via SecAuditLog before this + directive may be used. Additionally, this log is only used for + replicating the main audit log index file when concurrent audit logging + is used. It will not be used for non-concurrent + audit logging. +
+ +
+ <literal>SecAuditLogDirMode</literal> + + Description: Configures the mode + (permissions) of any directories created for concurrent audit logs using + an octal mode (as used in chmod). See SecAuditLogFileMode for controlling the mode + of audit log files. + + Syntax: SecAuditLogDirMode octal_mode|"default" + + Example Usage: SecAuditLogDirMode 02750 + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.10 + + Dependencies/Notes: This feature is not + available on operating systems not supporting octal file modes. The + default mode (0600) only grants read/write access to the account writing + the file. If access from another account is needed (using mpm-itk is a + good example), then this directive may be required. However, use this + directive with caution to avoid exposing potentially sensitive data to + unauthorized users. Using the value "default" will revert back to the + default setting. + + + The process umask may still limit the mode if it is being more + restrictive than the mode set using this directive. + +
+ +
+ <literal>SecAuditLogFileMode</literal> + + Description: Configures the mode + (permissions) of any files created for concurrent audit logs using an + octal mode (as used in chmod). See SecAuditLogDirMode for controlling the mode of + created audit log directories. + + Syntax: SecAuditLogFileMode + octal_mode|"default" + + Example Usage: SecAuditLogFileMode 00640 + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.10 + + Dependencies/Notes: This feature is not + available on operating systems not supporting octal file modes. The + default mode (0600) only grants read/write access to the account writing + the file. If access from another account is needed (using mpm-itk is a + good example), then this directive may be required. However, use this + directive with caution to avoid exposing potentially sensitive data to + unauthorized users. Using the value "default" will revert back to the + default setting. + + + The process umask may still limit the mode if it is being more + restrictive than the mode set using this directive. + +
+ +
+ <literal>SecAuditLogParts</literal> + + Description: Defines which part of each + transaction are going to be recorded in audit log. Each part is assigned + a single letter. If a letter appears in the list then the equivalent + part of each transactions will be recorded. See below for the list of + all parts. + + Syntax: SecAuditLogParts PARTS + + Example Usage: SecAuditLogParts ABCFHZ + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: At this time ModSecurity + does not log response bodies of stock Apache responses (e.g. 404), or the Server and Date response headers. + + Default: ABCFHZ. + + + Please refer to the ModSecurity Data Formats document for a + detailed description of every available part. + + + Available audit log parts: + + + + A - audit log header + (mandatory) + + + + B - request headers + + + + C - request body (present + only if the request body exists and ModSecurity is configured to + intercept it) + + + + D - RESERVED for + intermediary response headers, not implemented yet. + + + + E - intermediary response + body (present only if ModSecurity is configured to intercept + response bodies, and if the audit log engine is configured to record + it). Intermediary response body is the same as the actual response + body unless ModSecurity intercepts the intermediary response body, + in which case the actual response body will contain the error + message (either the Apache default error message, or the + ErrorDocument page). + + + + F - final response headers + (excluding the Date and Server headers, which are always added by + Apache in the late stage of content delivery). + + + + G - RESERVED for the actual + response body, not implemented yet. + + + + H - audit log + trailer + + + + I - This part is a + replacement for part C. It will log the same data as C in all cases + except when multipart/form-data + encoding in used. In this case it will log a fake application/x-www-form-urlencoded body + that contains the information about parameters but not about the + files. This is handy if you don't want to have (often large) files + stored in your audit logs. + + + + J - RESERVED. This part, + when implemented, will contain information about the files uploaded + using multipart/form-data encoding. + + + + K - This part contains a + full list of every rule that matched (one per line) in the order + they were matched. The rules are fully qualified and will thus show + inherited actions and default operators. Supported as of + v2.5.0 + + + + Z - final boundary, + signifies the end of the entry (mandatory) + + +
+ +
+ <literal>SecAuditLogRelevantStatus</literal> + + Description: Configures which response status + code is to be considered relevant for the purpose of audit + logging. + + Syntax: SecAuditLogRelevantStatus REGEX + + Example Usage: SecAuditLogRelevantStatus + ^(?:5|4\d[^4]) + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: Must have the + SecAuditEngine set to + RelevantOnly. The parameter is a regular + expression. + + The main purpose of this directive is to allow you to configure + audit logging for only transactions that generate the specified HTTP + Response Status Code. This directive is often used to the decrease the + total size of the audit log file. Keep in mind that if this parameter is + used, then successful attacks that result in a 200 OK status code will + not be logged. +
+ +
+ <literal>SecAuditLogStorageDir</literal> + + Description: Configures the storage directory + where concurrent audit log entries are to be stored. + + Syntax: SecAuditLogStorageDir + /path/to/storage/dir + + Example Usage: SecAuditLogStorageDir + /usr/local/apache/logs/audit + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: SecAuditLogType must be + set to Concurrent. The directory must already be created before starting + Apache and it must be writable by the web server user as new files are + generated at runtime. + + As with all logging mechanisms, ensure that you specify a file + system location that has adequate disk space and is not on the root + partition. +
+ +
+ <literal>SecAuditLogType</literal> + + Description: Configures the type of audit + logging mechanism to be used. + + Syntax: SecAuditLogType Serial|Concurrent + + Example Usage: SecAuditLogType Serial + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: Must specify + SecAuditLogStorageDir if you use concurrent + logging. + + Possible values are: + + + + Serial - all audit log + entries will be stored in the main audit logging file. This is more + convenient for casual use but it is slower as only one audit log + entry can be written to the file at any one file. + + + + Concurrent - audit log + entries will be stored in separate files, one for each transaction. + Concurrent logging is the mode to use if you are going to send the + audit log data off to a remote ModSecurity Console host. + + +
+ +
+ <literal>SecCacheTransformations</literal> + (Deprecated/Experimental) + + Description: Controls caching of + transformations. Caching is off by default starting with 2.5.6, when it + was deprecated and downgraded back to experimental. + + Syntax: SecCacheTransformations On|Off + [options] + + Example Usage: SecCacheTransformations On + "minlen:64,maxlen:0" + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: N/A + + First parameter: + + + + On - cache transformations + (per transaction, per phase) allowing identical transformations to + be performed only once. (default) + + + + Off - do not cache any + transformations, forcing all transformations to be performed for + each rule executed. + + + + The following options are allowed (comma separated): + + + + incremental:on|off - + enabling this option will cache every transformation instead of just + the final transformation. (default: off) + + + + maxitems:N - do not allow + more than N transformations to be cached. The cache will then be + disabled. A zero value is interpreted as "unlimited". This option + may be useful to limit caching for a form with a large number of + ARGS. (default: 512) + + + + minlen:N - do not cache the + transformation if the value's length is less than N bytes. (default: + 32) + + + + maxlen:N - do not cache the + transformation if the value's length is more than N bytes. A zero + value is interpreted as "unlimited". (default: 1024) + + +
+ +
+ <literal>SecChrootDir</literal> + + Description: Configures the directory path + that will be used to jail the web server process. + + Syntax: SecChrootDir + /path/to/chroot/dir + + Example Usage: SecChrootDir /chroot + + Processing Phase: N/A + + Scope: Main + + Version: 2.0.0 + + Dependencies/Notes: This feature is not + available on Windows builds. The internal chroot functionality provided + by ModSecurity works great for simple setups. One example of a simple + setup is Apache serving static files only, or running scripts using + modules.builds. Some problems you might encounter with more complex + setups: + + + + DNS lookups do not work (this is because this feature requires + a shared library that is loaded on demand, after chroot takes + place). + + + + You cannot send email from PHP because it uses sendmail and + sendmail is outside the jail. + + + + In some cases Apache graceful (reload) no longer works. + + + + You should be aware that the internal chroot feature might not be + 100% reliable. Due to the large number of default and third-party + modules available for the Apache web server, it is not possible to + verify the internal chroot works reliably with all of them. A module, + working from within Apache, can do things that make it easy to break out + of the jail. In particular, if you are using any of the modules that + fork in the module initialisation phase (e.g. + mod_fastcgi, mod_fcgid, + mod_cgid), you are advised to examine each Apache + process and observe its current working directory, process root, and the + list of open files. Consider what your options are and make your own + decision. +
+ +
+ <literal>SecComponentSignature</literal> + + Description: Appends component signature to + the ModSecurity signature. + + Syntax: SecComponentSignature + "COMPONENT_NAME/X.Y.Z (COMMENT)" + + Example usage: SecComponentSignature + "Core Rules/1.2.3" + + Processing Phase: N/A + + Scope: Main + + Version: 2.5.0 + + Dependencies/Notes: This directive should be + used to make the presence of significant ModSecurity components known. + The entire signature will be recorded in transaction audit log. It + should be used by ModSecurity module and rule set writers to make + debugging easier. +
+ +
+ <literal>SecContentInjection</literal> + + Description: Enables content injection using + actions append and prepend. + + Syntax: SecContentInjection + (On|Off) + + Example Usage: SecContentInjection + On + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: N/A +
+ +
+ <literal>SecCookieFormat</literal> + + Description: Selects the cookie format that + will be used in the current configuration context. + + Syntax: SecCookieFormat 0|1 + + Example Usage: SecCookieFormat 0 + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: None + + Possible values are: + + + + 0 - use version 0 + (Netscape) cookies. This is what most applications use. It is the + default value. + + + + 1 - use version 1 + cookies. + + +
+ +
+ <literal>SecDataDir</literal> + + Description: Path where persistent data (e.g. + IP address data, session data, etc) is to be stored. + + Syntax: SecDataDir + /path/to/dir + + Example Usage: SecDataDir /usr/local/apache/logs/data + + Processing Phase: N/A + + Scope: Main + + Dependencies/Notes: This directive is needed + when initcol, setsid an setuid are used. Must be writable by the web + server user. +
+ +
+ <literal>SecDebugLog</literal> + + Description: Path to the ModSecurity debug + log file. + + Syntax: SecDebugLog + /path/to/modsec-debug.log + + Example Usage: SecDebugLog + /usr/local/apache/logs/modsec-debug.log + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: None +
+ +
+ <literal>SecDebugLogLevel</literal> + + Description: Configures the verboseness of + the debug log data. + + Syntax: SecDebugLogLevel 0|1|2|3|4|5|6|7|8|9 + + Example Usage: SecDebugLogLevel 4 + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: Levels 1 - 3 are always sent to the Apache error log. + Therefore you can always use level 0 + as the default logging level in production. Level 5 is useful when debugging. It is not + advisable to use higher logging levels in production as excessive + logging can slow down server significantly. + + Possible values are: + + + + 0 - no logging. + + + + 1 - errors (intercepted + requests) only. + + + + 2 - warnings. + + + + 3 - notices. + + + + 4 - details of how + transactions are handled. + + + + 5 - as above, but including + information about each piece of information handled. + + + + 9 - log everything, + including very detailed debugging information. + + +
+ +
+ <literal>SecDefaultAction</literal> + + Description: Defines the default action to + take on a rule match. + + Syntax: SecDefaultAction + action1,action2,action3 + + Example Usage: SecDefaultAction + log,auditlog,deny,status:403,phase:2 + + Processing Phase: Any + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: Rules following a + SecDefaultAction directive will inherit this setting + unless a specific action is specified for an individual rule or until + another SecDefaultAction is specified. Take special + note that in the logging disruptive actions are not allowed, but this + can inadvertently be inherited using a disruptive action in + SecDefaultAction. + + The default value is minimal (differing from previous + versions): + + SecDefaultAction phase:2,log,auditlog,pass + + + SecDefaultAction must specify a disruptive + action and a processing phase and cannot contain metadata + actions. + + + + SecDefaultAction is not + inherited across configuration contexts. (For an example of why this + may be a problem for you, read the following ModSecurity Blog entry + http://blog.modsecurity.org/2008/07/modsecurity-tri.html). + +
+ +
+ <literal>SecGeoLookupDb</literal> + + Description: Defines the path to the + geographical database file. + + Syntax: SecGeoLookupDb /path/to/db + + Example Usage: SecGeoLookupDb + /usr/local/geo/data/GeoLiteCity.dat + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: Check out + maxmind.com for free database files. +
+ +
+ <literal>SecGuardianLog</literal> + + Description: Configuration directive to use + the httpd-guardian script to monitor for Denial of Service (DoS) + attacks. + + Syntax: SecGuardianLog |/path/to/httpd-guardian + + Example Usage: SecGuardianLog + |/usr/local/apache/bin/httpd-guardian + + Processing Phase: N/A + + Scope: Main + + Version: 2.0.0 + + Dependencies/Notes: By default httpd-guardian + will defend against clients that send more than 120 requests in a + minute, or more than 360 requests in five minutes. + + Since 1.9, ModSecurity supports a new directive, SecGuardianLog, + that is designed to send all access data to another program using the + piped logging feature. Since Apache is typically deployed in a + multi-process fashion, making information sharing difficult, the idea is + to deploy a single external process to observe all requests in a + stateful manner, providing additional protection. + + Development of a state of the art external protection tool will be + a focus of subsequent ModSecurity releases. However, a fully functional + tool is already available as part of the Apache httpd tools + project. The tool is called httpd-guardian and can be used to + defend against Denial of Service attacks. It uses the blacklist tool + (from the same project) to interact with an iptables-based (Linux) or + pf-based (*BSD) firewall, dynamically blacklisting the offending IP + addresses. It can also interact with SnortSam (http://www.snortsam.net). + Assuming httpd-guardian is already configured (look into the source code + for the detailed instructions) you only need to add one line to your + Apache configuration to deploy it: + + SecGuardianLog |/path/to/httpd-guardian +
+ +
+ <literal>SecMarker</literal> + + Description: Adds a fixed rule marker in the + ruleset to be used as a target in a skipAfter action. + A SecMarker directive essentially creates a rule that + does nothing and whose only purpose it to carry the given ID. + + Syntax: SecMarker + ID + + Example Usage: SecMarker 9999 + + Processing Phase: Any + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: None + + SecRule REQUEST_URI "^/$" \ + "chain,t:none,t:urlDecode,t:lowercase,t:normalisePath,skipAfter:99" +SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" +SecRule REQUEST_HEADERS:User-Agent \ + "^Apache \(internal dummy connection\)$" "t:none" +SecRule &REQUEST_HEADERS:Host "@eq 0" \ + "deny,log,status:400,id:08,severity:4,msg:'Missing a Host Header'" +SecRule &REQUEST_HEADERS:Accept "@eq 0" \ + "log,deny,log,status:400,id:15,msg:'Request Missing an Accept Header'" + +SecMarker 99 +
+ +
+ <literal>SecPcreMatchLimit</literal> + + Description:Sets the the match limit in the + PCRE library. See the pcre_extra field in the pcreapi man page. + + Syntax: SecPcreMatchLimit value + + Example Usage: SecPcreMatchLimit 1500 + + Processing Phase: N/A + + Scope: Global + + Version: 2.5.13 + + Dependencies/Notes: Default is set at compile + (1500 by default). See also + SecPcreMatchLimitRecursion + + If the limits are exceeded this will be logged at level 3 in the + debug log, added as a Message line in the audit log and the TX:MSC_PCRE_LIMITS_EXCEEDED flag will be set + to a non-zero value. To prevent bypass, you should write a rule to check + for the existance of the TX:MSC_PCRE_LIMITS_EXCEEDED flag. + + SecPcreMatchLimit 100 +SecPcreMatchLimitRecursion 100 + ... +SecRule TX:/^MSC_/ "!@eq 0" "phase:5,pass,log,auditlog,msg:'Potential REDoS'" + + + The --enable-pcre-match-limit=val configure + option will set a custom default and the + --disable-pcre-match-limit option will resort to + the compiled PCRE library default. + +
+ +
+ <literal>SecPcreMatchLimitRecursion</literal> + + Description:Sets the the match limit + recursion in the PCRE library. See the pcre_extra field in the pcreapi + man page. + + Syntax: SecPcreMatchLimitRecursion value + + Example Usage: SecPcreMatchLimitRecursion 1500 + + Processing Phase: N/A + + Scope: Global + + Version: 2.5.13 + + Dependencies/Notes: Default is set at compile + (1500 by default). See also SecPcreMatchLimit + + If the limits are exceeded this will be logged at level 3 in the + debug log, added as a Message line in the audit log and the TX:MSC_PCRE_LIMITS_EXCEEDED flag will be set + to a non-zero value. To prevent bypass, you should write a rule to check + for the existance of the TX:MSC_PCRE_LIMITS_EXCEEDED flag. + + SecPcreMatchLimit 100 +SecPcreMatchLimitRecursion 100 + ... +SecRule TX:/^MSC_/ "!@eq 0" "phase:5,pass,log,auditlog,msg:'Potential REDoS'" + + + The --enable-pcre-match-limit-recursion=val + configure option will set a custom default and the + --disable-pcre-match-limit-recursion option will + resort to the compiled PCRE library default. + +
+ +
+ <literal>SecPdfProtect</literal> + + Description: Enables the PDF XSS protection + functionality. Once enabled access to PDF files is tracked. Direct + access attempts are redirected to links that contain one-time tokens. + Requests with valid tokens are allowed through unmodified. Requests with + invalid tokens are also allowed through but with forced download of the + PDF files. This implementation uses response headers to detect PDF files + and thus can be used with dynamically generated PDF files that do not + have the .pdf extension in the request URI. + + Syntax: SecPdfProtect On|Off + + Example Usage: SecPdfProtect On + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: None +
+ +
+ <literal>SecPdfProtectMethod</literal> + + Description: Configure desired protection + method to be used when requests for PDF files are detected. Possible + values are TokenRedirection and + ForcedDownload. The token redirection approach will + attempt to redirect with tokens where possible. This allows PDF files to + continue to be opened inline but only works for GET requests. Forced + download always causes PDF files to be delivered as opaque binaries and + attachments. The latter will always be used for non-GET requests. Forced + download is considered to be more secure but may cause usability + problems for users ("This PDF won't open anymore!"). + + Syntax: SecPdfProtectMethod method + + Example Usage: SecPdfProtectMethod TokenRedirection + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: None + + Default: + TokenRedirection +
+ +
+ <literal>SecPdfProtectSecret</literal> + + Description: Defines the secret that will be + used to construct one-time tokens. You should use a reasonably long + value for the secret (e.g. 16 characters is good). Once selected the + secret should not be changed as it will break the tokens that were sent + prior to change. But it's not a big deal even if you change it. It will + just force download of PDF files with tokens that were issued in the + last few seconds. + + Syntax: SecPdfProtectSecret secret + + Example Usage: SecPdfProtectSecret + MyRandomSecretString + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: None +
+ +
+ <literal>SecPdfProtectTimeout</literal> + + Description: Defines the token timeout. After + token expires it can no longer be used to allow access to PDF file. + Request will be allowed through but the PDF will be delivered as + attachment. + + Syntax: SecPdfProtectTimeout timeout + + Example Usage: SecPdfProtectTimeout 10 + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: None + + Default: 10 +
+ +
+ <literal>SecPdfProtectTokenName</literal> + + Description: Defines the name of the token. + The only reason you would want to change the name of the token is if you + wanted to hide the fact you are running ModSecurity. It's a good reason + but it won't really help as the adversary can look into the algorithm + used for PDF protection and figure it out anyway. It does raise the bar + slightly so go ahead if you want to. + + Syntax: SecPdfProtectTokenName name + + Example Usage: SecPdfProtectTokenName PDFTOKEN + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: None + + Default: PDFTOKEN +
+ +
+ <literal>SecRequestBodyAccess</literal> + + Description: Configures whether request + bodies will be buffered and processed by ModSecurity by default. + + Syntax: SecRequestBodyAccess On|Off + + Example Usage: SecRequestBodyAccess On + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: This directive is + required if you plan to inspect POST_PAYLOAD. This + directive must be used along with the "phase:2" processing phase action + and REQUEST_BODY variable/location. If any of these 3 + parts are not configured, you will not be able to inspect the request + bodies. + + Possible values are: + + + + On - access request + bodies. + + + + Off - do not attempt to + access request bodies. + + +
+ +
+ <literal>SecRequestBodyLimit</literal> + + Description: Configures the maximum request + body size ModSecurity will accept for buffering. + + Syntax: SecRequestBodyLimit NUMBER_IN_BYTES + + Example Usage: SecRequestBodyLimit 134217728 + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: 131072 KB (134217728 + bytes) is the default setting. Anything over this limit will be rejected + with status code 413 Request Entity Too Large. There is a hard limit of + 1 GB. +
+ +
+ <literal>SecRequestBodyNoFilesLimit</literal> + + Description: Configures the maximum request + body size ModSecurity will accept for buffering, excluding the size of + files being transported in the request. This directive comes handy to + further reduce susceptibility to DoS attacks when someone is sending + request bodies of very large sizes. Web applications that require file + uploads must configure SecRequestBodyLimit to a high + value. Since large files are streamed to disk file uploads will not + increase memory consumption. However, it's still possible for someone to + take advantage of a large request body limit and send non-upload + requests with large body sizes. This directive eliminates that + loophole. + + Syntax: SecRequestBodyNoFilesLimit + NUMBER_IN_BYTES + + Example Usage: SecRequestBodyLimit 131072 + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: 1 MB (1048576 bytes) is + the default setting. This value is very conservative. For most + applications you should be able to reduce it down to 128 KB or lower. + Anything over the limit will be rejected with status code 413 + Request Entity Too Large. There is a hard limit of 1 + GB. +
+ +
+ <literal>SecRequestBodyInMemoryLimit</literal> + + Description: Configures the maximum request + body size ModSecurity will store in memory. + + Syntax: SecRequestBodyInMemoryLimit + NUMBER_IN_BYTES + + Example Usage: SecRequestBodyInMemoryLimit 131072 + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: None + + By default the limit is 128 KB: + + # Store up to 128 KB in memory +SecRequestBodyInMemoryLimit 131072 +
+ +
+ <literal>SecResponseBodyLimit</literal> + + Description: Configures the maximum response + body size that will be accepted for buffering. + + Syntax: SecResponseBodyLimit NUMBER_IN_BYTES + + Example Usage: SecResponseBodyLimit 524228 + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: Anything over this limit + will be rejected with status code 500 Internal Server Error. This + setting will not affect the responses with MIME types that are not + marked for buffering. There is a hard limit of 1 GB. + + By default this limit is configured to 512 KB: + + # Buffer response bodies of up to 512 KB in length +SecResponseBodyLimit 524288 +
+ +
+ <literal>SecResponseBodyLimitAction</literal> + + Description: Controls what happens once a + response body limit, configured with + SecResponseBodyLimit, is encountered. By default + ModSecurity will reject a response body that is longer than specified. + Some web sites, however, will produce very long responses making it + difficult to come up with a reasonable limit. Such sites would have to + raise the limit significantly to function properly defying the purpose + of having the limit in the first place (to control memory consumption). + With the ability to choose what happens once a limit is reached site + administrators can choose to inspect only the first part of the + response, the part that can fit into the desired limit, and let the rest + through. Some could argue that allowing parts of responses to go + uninspected is a weakness. This is true in theory but only applies to + cases where the attacker controls the output (e.g. can make it arbitrary + long). In such cases, however, it is not possible to prevent leakage + anyway. The attacker could compress, obfuscate, or even encrypt data + before it is sent back, and therefore bypass any monitoring + device. + + Syntax: SecResponseBodyLimitAction + Reject|ProcessPartial + + Example Usage: + SecResponseBodyLimitAction ProcessPartial + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: None +
+ +
+ <literal>SecResponseBodyMimeType</literal> + + Description: Configures which MIME types are to be considered for response + body buffering. + + Syntax: SecResponseBodyMimeType mime/type + + Example Usage: SecResponseBodyMimeType text/plain + text/html + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: Multiple SecResponseBodyMimeType directives can be + used to add MIME types. + + The default value is text/plaintext/html: + + SecResponseBodyMimeType text/plain text/html +
+ +
+ <literal>SecResponseBodyMimeTypesClear</literal> + + Description: Clears the list of MIME types considered for response body + buffering, allowing you to start populating the list from + scratch. + + Syntax: SecResponseBodyMimeTypesClear + + Example Usage: SecResponseBodyMimeTypesClear + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: None +
+ +
+ <literal>SecResponseBodyAccess</literal> + + Description: Configures whether response + bodies are to be buffer and analysed or not. + + Syntax: SecResponseBodyAccess On|Off + + Example Usage: SecResponseBodyAccess On + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: This directive is + required if you plan to inspect HTML responses. This directive must be + used along with the "phase:4" processing phase action and RESPONSE_BODY + variable/location. If any of these 3 parts are not configured, you will + not be able to inspect the response bodies. + + Possible values are: + + + + On - access response bodies + (but only if the MIME type matches, see above). + + + + Off - do not attempt to + access response bodies. + + +
+ +
+ <literal>SecRule</literal> + + Description: SecRule is the main ModSecurity directive. It + is used to analyse data and perform actions based on the results. + + Syntax: SecRule + VARIABLES OPERATOR [ACTIONS] + + Example Usage: SecRule REQUEST_URI "attack" \ + + + "phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath" + + Processing Phase: Any + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: None + + In general, the format of this rule is as follows: + + SecRule VARIABLES OPERATOR [ACTIONS] + + The second part, OPERATOR, + specifies how they are going to be checked. The third (optional) part, + ACTIONS, specifies what to do + whenever the operator used performs a successful match against a + variable. + +
+ Variables in rules + + The first part, VARIABLES, + specifies which variables are to be checked. For example, the + following rule will reject a transaction that has the word + dirty in the URI: + + SecRule ARGS dirty + + Each rule can specify one or more variables: + + SecRule ARGS|REQUEST_HEADERS:User-Agent dirty + + There is a third format supported by the selection operator - + XPath expression. XPath expressions can only used against the special + variable XML, which is available only of the request body was + processed as XML. + + SecRule XML:/xPath/Expression dirty + + + Not all collections support all selection operator format + types. You should refer to the documentation of each collection to + determine what is and isn't supported. + +
+ +
+ Collections + + A variable can contain one or many pieces of data, depending on + the nature of the variable and the way it is used. We've seen examples + of both approaches in the previous section. When a variable can + contain more than one value we refer to it as a + collection. + + Collections are always expanded before a rule is run. For + example, the following rule: + + SecRule ARGS dirty + + will be expanded to: + + SecRule ARGS:p dirty +SecRule ARGS:q dirty + + in a requests that has only two parameters, named + p and q. + + Collections come in several flavours: + + + + Read-only + + + Created at runtime using transaction data. For example: + ARGS (contains a list of all request + parameter values) and REQUEST_HEADERS + (contains a list of all request header values). + + + + + Transient Read/Write + + + The TX collection is created (empty) + for every transaction. Rules can read from it and write to it + (using the setvar action, for example), but + the information stored in this collection will not survive the + end of transaction. + + + + + Persistent Read/Write + + + There are several collections that can be written to, but + which are persisted to the storage backend. These collections + are used to track clients across transactions. Examples of + collections that fall into this type are IP, + SESSION and USER. + + + +
+ +
+ Operators in rules + + In the simplest possible case you will use a regular expression + pattern as the second rule parameter. This is what we've done in the + examples above. If you do this ModSecurity assumes you want to use the + rx (regular expression) operator. + You can also explicitly specify the operator you want to use by using + @, followed by the name of an + operator, at the beginning of the second SecRule + parameter: + + SecRule ARGS "@rx dirty" + + Note how we had to use double quotes to delimit the second rule + parameter. This is because the second parameter now has whitespace in + it. Any number of whitespace characters can follow the name of the + operator. If there are any non-whitespace characters there, they will + all be treated as a special parameter to the operator. In the case of + the regular expression operator the special parameter is the pattern + that will be used for comparison. + + The @ can be the second character if you are using negation to + negate the result returned by the operator: + + SecRule &ARGS "!@rx ^0$" +
+ +
+ Operator negation + + Operator results can be negated by using an exclamation mark at + the beginning of the second parameter. The following rule matches if + the word dirty does not appear + in the User-Agent request header: + + SecRule REQUEST_HEADERS:User-Agent !dirty + + You can use the exclamation mark in combination with any + parameter. If you do, the exclamation mark needs to go first, followed + by the explicit operator reference. The following rule has the same + effect as the previous example: + + SecRule REQUEST_HEADERS:User-Agent "!@rx dirty" + + If you need to use negation in a rule that is going to be + applied to several variables then it may not be immediately clear what + will happen. Consider the following example: + + SecRule ARGS:p|ARGS:q !dirty + + The above rule is identical to: + + SecRule ARGS:p !dirty +SecRule ARGS:q !dirty + + + Negation is applied to operations against individual + operations, not agains the entire variable list. + +
+ +
+ Actions in rules + + The third parameter, ACTIONS, + can be omitted only because there is a helper feature that specifies + the default action list. If the parameter isn't omitted the actions + specified in the parameter will be merged with the default action list + to create the actual list of actions that will be processed on a rule + match. +
+
+ +
+ <literal>SecRuleInheritance</literal> + + Description: Configures whether the current + context will inherit rules from the parent context (configuration + options are inherited in most cases - you should look up the + documentation for every directive to determine if it is inherited or + not). + + Syntax: SecRuleInheritance On|Off + + Example Usage: SecRuleInheritance Off + + Processing Phase: Any + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: Resource-specific + contexts (e.g. Location, Directory, etc) cannot override + phase1 rules configured in the main server or in + the virtual server. This is because phase 1 is run early in the request + processing process, before Apache maps request to resource. Virtual host + context can override phase 1 rules configured in the main server. + + Example: The following example shows where ModSecurity may be + enabled in the main Apache configuration scope, however you might want + to configure your VirtualHosts differently. In the first example, the + first VirtualHost is not inheriting the ModSecurity main config + directives and in the second one it is. + + SecRuleEngine On +SecDefaultAction log,pass,phase:2 +... + +<VirtualHost *:80> +ServerName app1.com +ServerAlias www.app1.com +SecRuleInheritance Off +SecDefaultAction log,deny,phase:1,redirect:http://www.site2.com +... +</VirtualHost> + +<VirtualHost *:80> +ServerName app2.com +ServerAlias www.app2.com +SecRuleInheritance On SecRule ARGS "attack" +... +</VirtualHost> + + Possible values are: + + + + On - inherit rules from the + parent context. + + + + Off - do not inherit rules + from the parent context. + + + Configuration contexts are an Apache concept. Directives + <Directory>, + <Files>, + <Location> and + <VirtualHost> are all used to create + configuration contexts. For more information please go to the + Apache documentation section Configuration + Sections. + + + +
+ +
+ <literal>SecRuleEngine</literal> + + Description: Configures the rules + engine. + + Syntax: SecRuleEngine On|Off|DetectionOnly + + Example Usage: SecRuleEngine On + + Processing Phase: Any + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: This directive can also + be controlled by the ctl action (ctl:ruleEngine=off) for per rule + processing. + + Possible values are: + + + + On - process rules. + + + + Off - do not process + rules. + + + + DetectionOnly - process + rules but never intercept transactions, even when rules are + configured to do so. + + +
+ +
+ <literal>SecRuleRemoveById</literal> + + Description: Removes matching rules from the + parent contexts. + + Syntax: SecRuleUpdateActionById RULEID + ACTIONLIST + + Example Usage: SecRuleRemoveByID 1 2 "9000-9010" + + Processing Phase: Any + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: This directive supports + multiple parameters, where each parameter can either be a rule ID, or a + range. Parameters that contain spaces must be delimited using double + quotes. + + SecRuleRemoveById 1 2 5 10-20 "400-556" 673 +
+ +
+ <literal>SecRuleRemoveByMsg</literal> + + Description: Removes matching rules from the + parent contexts. + + Syntax: SecRuleRemoveByMsg REGEX + + Example Usage: SecRuleRemoveByMsg "FAIL" + + Processing Phase: Any + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: This directive supports + multiple parameters. Each parameter is a regular expression that will be + applied to the message (specified using the msg action). +
+ +
+ <literal>SecRuleScript</literal> (Experimental) + + Description: This directive creates a special + rule that executes a Lua script to decide whether to match or not. The + main difference from SecRule is that there are no + targets nor operators. The script can fetch any variable from the + ModSecurity context and use any (Lua) operator to test them. The second + optional parameter is the list of actions whose meaning is identical to + that of SecRule. + + Syntax: SecRuleScript + /path/to/script.lua [ACTIONS] + + Example Usage: SecRuleScript "/path/to/file.lua" + "block" + + Processing Phase: Any + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: None + + + All Lua scripts are compiled at configuration time and cached in + memory. To reload scripts you must reload the entire ModSecurity + configuration by restarting Apache. + + + Example script: + + -- Your script must define the main entry +-- point, as below. +function main() + -- Log something at level 1. Normally you shouldn't be + -- logging anything, especially not at level 1, but this is + -- just to show you can. Useful for debugging. + m.log(1, "Hello world!"); + + -- Retrieve one variable. + local var1 = m.getvar("REMOTE_ADDR"); + + -- Retrieve one variable, applying one transformation function. + -- The second parameter is a string. + local var2 = m.getvar("ARGS", "lowercase"); + + -- Retrieve one variable, applying several transformation functions. + -- The second parameter is now a list. You should note that m.getvar() + -- requires the use of comma to separate collection names from + -- variable names. This is because only one variable is returned. + local var3 = m.getvar("ARGS.p", { "lowercase", "compressWhitespace" } ); + + -- If you want this rule to match return a string + -- containing the error message. The message must contain the name + -- of the variable where the problem is located. + -- return "Variable ARGS:p looks suspicious!" + + -- Otherwise, simply return nil. + return nil; +end + + In this first example we were only retrieving one variable at the + time. In this case the name of the variable is known to you. In many + cases, however, you will want to examine variables whose names you won't + know in advance, for example script parameters. + + Example showing use of m.getvars() to retrieve + many variables at once: + + function main() + -- Retrieve script parameters. + local d = m.getvars("ARGS", { "lowercase", "htmlEntityDecode" } ); + + -- Loop through the paramters. + for i = 1, #d do + -- Examine parameter value. + if (string.find(d[i].value, "<script")) then + -- Always specify the name of the variable where the + -- problem is located in the error message. + return ("Suspected XSS in variable " .. d[i].name .. "."); + end + end + + -- Nothing wrong found. + return nil; +end + + + Go to http://www.lua.org/ to find more + about the Lua programming language. The reference manual too is + available online, at http://www.lua.org/manual/5.1/. + + + + Lua support is marked as experimental as + the way the progamming interface may continue to evolve while we are + working for the best implementation style. Any user input into the + programming interface is appreciated. + +
+ +
+ <literal>SecRuleUpdateActionById</literal> + + Description: Updates the action list of the + specified rule. + + Syntax: SecRuleRemoveById RULEID ACTIONLIST + + Example Usage: SecRuleUpdateActionById 12345 + deny,status:403 + + Processing Phase: Any + + Scope: Any + + Version: 2.5.0 + + Dependencies/Notes: This directive merges the + specified action list with the rule's action list. There are two + limitations. The rule ID cannot be changed, nor can the phase. Further + note that actions that may be specified multiple times are appended to + the original. + + SecAction \ + "t:lowercase,phase:2,id:12345,pass,msg:'The Message',log,auditlog" +SecRuleUpdateActionById 12345 "t:compressWhitespace,deny,status:403,msg:'A new message' + + The example above will cause the rule to be executed as if it was + specified as follows: + + SecAction \ + "t:lowercase,phase:2,id:12345,log,auditlog,t:compressWhitespace,deny,status:403,msg:'A new message'" +
+ +
+ <literal>SecServerSignature</literal> + + Description: Instructs ModSecurity to change + the data presented in the "Server:" response header token. + + Syntax: SecServerSignature "WEB SERVER + SOFTWARE" + + Example Usage: SecServerSignature + "Netscape-Enterprise/6.0" + + Processing Phase: N/A + + Scope: Main + + Version: 2.0.0 + + Dependencies/Notes: In order for this + directive to work, you must set the Apache ServerTokens directive to + Full. ModSecurity will overwrite the server signature data held in this + memory space with the data set in this directive. If ServerTokens is not + set to Full, then the memory space is most likely not large enough to + hold the new data we are looking to insert. +
+ +
+ <literal>SecTmpDir</literal> + + Description: Configures the directory where + temporary files will be created. + + Syntax: SecTmpDir + /path/to/dir + + Example Usage: SecTmpDir /tmp + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: Needs to be writable by + the Apache user process. This is the directory location where Apache + will swap data to disk if it runs out of memory (more data than what was + specified in the SecRequestBodyInMemoryLimit directive) during + inspection. +
+ +
+ <literal>SecUploadDir</literal> + + Description: Configures the directory where + intercepted files will be stored. + + Syntax: SecUploadDir + /path/to/dir + + Example Usage: SecUploadDir /tmp + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: This directory must be on + the same filesystem as the temporary directory defined with SecTmpDir. This directive is used with + SecUploadKeepFiles. +
+ +
+ <literal>SecUploadFileLimit</literal> + + Description: Configures the maximum number of + file uploads processed in a multipart POST. + + Syntax: SecUploadFileLimit number + + Example Usage: SecUploadFileLimit 10 + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.13 + + Dependencies/Notes: The default is set to 100 + files, but you are encouraged to reduce this value. Any file over the + limit will not be extracted and the MULTIPART_FILE_LIMIT_EXCEEDED and MULTIPART_STRICT_ERROR flags will be set. To + prevent bypassing any file checks, you must check for one of these + flags. + + + If the limit is exceeded, the part name and file name will still + be recorded in FILES_NAME and + FILES, the file size will be + recorded in FILES_SIZES, but there + will be no record in FILES_TMPNAMES + as a temporary file was not created. + +
+ +
+ <literal>SecUploadFileMode</literal> + + Description: Configures the mode + (permissions) of any uploaded files using an octal mode (as used in + chmod). + + Syntax: SecUploadFileMode octal_mode|"default" + + Example Usage: SecUploadFileMode 0640 + + Processing Phase: N/A + + Scope: Any + + Version: 2.1.6 + + Dependencies/Notes: This feature is not + available on operating systems not supporting octal file modes. The + default mode (0600) only grants read/write access to the account writing + the file. If access from another account is needed (using clamd is a + good example), then this directive may be required. However, use this + directive with caution to avoid exposing potentially sensitive data to + unauthorized users. Using the value "default" will revert back to the + default setting. + + + The process umask may still limit the mode if it is being more + restrictive than the mode set using this directive. + +
+ +
+ <literal>SecUploadKeepFiles</literal> + + Description: Configures whether or not the + intercepted files will be kept after transaction is processed. + + Syntax: SecUploadKeepFiles On|Off|RelevantOnly + + Example Usage: SecUploadKeepFiles On + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: This directive requires + the storage directory to be defined (using SecUploadDir). + + Possible values are: + + + + On - Keep uploaded + files. + + + + Off - Do not keep uploaded + files. + + + + RelevantOnly - This will + keep only those files that belong to requests that are deemed + relevant. + + +
+ +
+ <literal>SecWebAppId</literal> + + Description: Creates a partition on the + server that belongs to one web application. + + Syntax: SecWebAppId + "NAME" + + Example Usage: SecWebAppId "WebApp1" + + Processing Phase: N/A + + Scope: Any + + Version: 2.0.0 + + Dependencies/Notes: Partitions are used to + avoid collisions between session IDs and user IDs. This directive must + be used if there are multiple applications deployed on the same server. + If it isn't used, a collision between session IDs might occur. The + default value is default. + Example: + + <VirtualHost *:80> +ServerName app1.com +ServerAlias www.app1.com +SecWebAppId "App1" +SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass +SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} +... +</VirtualHost> + +<VirtualHost *:80> +ServerName app2.com +ServerAlias www.app2.com +SecWebAppId "App2" +SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass +SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} +... +</VirtualHost> + + In the two examples configurations shown, SecWebAppId is being + used in conjunction with the Apache VirtualHost directives. What this + achieves is to create more unique collection names when being hosted on + one server. Normally, when setsid is used, ModSecurity will create a + collection with the name "SESSION" and it will hold the value specified. + With using SecWebAppId as shown in the examples, however, the name of + the collection would become "App1_SESSION" and "App2_SESSION". + + SecWebAppId is relevant in two cases: + + + + You are logging transactions/alerts to the ModSecurity Console + and you want to use the web application ID to search only the + transactions belonging to that application. + + + + You are using the data persistence facility (collections + SESSION and USER) and you need to avoid collisions between sessions + and users belonging to different applications. + + +
+
+ +
+ Processing Phases + + ModSecurity 2.x allows rules to be placed in one of the following + five phases: + + + + Request headers (REQUEST_HEADERS) + + + + Request body (REQUEST_BODY) + + + + Response headers (RESPONSE_HEADERS) + + + + Response body (RESPONSE_BODY) + + + + Logging (LOGGING) + + + + Below is a diagram of the standard Apache Request Cycle. In the + diagram, the 5 ModSecurity processing phases are shown. + + + + In order to select the phase a rule executes during, use the phase + action either directly in the rule or in using the + SecDefaultAction directive: + + SecDefaultAction "log,pass,phase:2" +SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1" + + + Keep in mind that rules are executed according to phases, so even + if two rules are adjacent in a configuration file, but are set to + execute in different phases, they would not happen one after the other. + The order of rules in the configuration file is important only within + the rules of each phase. This is especially important when using the + skip and skipAfter actions. + + + + The LOGGING phase is special. It is executed at + the end of each transaction no matter what happened in the previous + phases. This means it will be processed even if the request was + intercepted or the allow action was used to pass the + transaction through. + + +
+ Phase Request Headers + + Rules in this phase are processed immediately after Apache + completes reading the request headers (post-read-request phase). At this + point the request body has not been read yet, meaning not all request + arguments are available. Rules should be placed in this phase if you + need to have them run early (before Apache does something with the + request), to do something before the request body has been read, + determine whether or not the request body should be buffered, or decide + how you want the request body to be processed (e.g. whether to parse it + as XML or not). + + Note + + Rules in this phase can not leverage Apache scope directives + (Directory, Location, LocationMatch, etc...) as the post-read-request + hook does not have this information yet. The exception here is the + VirtualHost directive. If you want to use ModSecurity rules inside + Apache locations, then they should run in Phase 2. Refer to the Apache + Request Cycle/ModSecurity Processing Phases diagram. +
+ +
+ Phase Request Body + + This is the general-purpose input analysis phase. Most of the + application-oriented rules should go here. In this phase you are + guaranteed to have received the request arguments (provided the request + body has been read). ModSecurity supports three encoding types for the + request body phase: + + + + application/x-www-form-urlencoded - used to + transfer form data + + + + multipart/form-data - used for file + transfers + + + + text/xml - used for passing XML data + + + + Other encodings are not used by most web applications. +
+ +
+ Phase Response Headers + + This phase takes place just before response headers are sent back + to the client. Run here if you want to observe the response before that + happens, and if you want to use the response headers to determine if you + want to buffer the response body. Note that some response status codes + (such as 404) are handled earlier in the request cycle by Apache and my + not be able to be triggered as expected. Additionally, there are some + response headers that are added by Apache at a later hook (such as Date, + Server and Connection) that we would not be able to trigger on or + sanitize. This should work appropriately in a proxy setup or within + phase:5 (logging). +
+ +
+ Phase Response Body + + This is the general-purpose output analysis phase. At this point + you can run rules against the response body (provided it was buffered, + of course). This is the phase where you would want to inspect the + outbound HTML for information disclosure, error messages or failed + authentication text. +
+ +
+ Phase Logging + + This phase is run just before logging takes place. The rules + placed into this phase can only affect how the logging is performed. + This phase can be used to inspect the error messages logged by Apache. + You cannot deny/block connections in this phase as it is too late. This + phase also allows for inspection of other response headers that weren't + available during phase:3 or phase:4. Note that you must be careful not + to inherit a disruptive action into a rule in this phase as this is a + configuration error in ModSecurity 2.5.0 and later versions. +
+
+ +
+ Variables + + The following variables are supported in ModSecurity 2.x: + +
+ <literal moreinfo="none">ARGS</literal> + + ARGS is a collection and can be used on its own + (means all arguments including the POST Payload), with a static + parameter (matches arguments with that name), or with a regular + expression (matches all arguments with name that matches the regular + expression). To look at only the query string or body arguments, see the + ARGS_GET and ARGS_POST + collections. + + Some variables are actually collections, which are expanded into + more variables at runtime. The following example will examine all + request arguments:SecRule ARGS dirty + Sometimes, however, you will want to look only at parts of a collection. + This can be achieved with the help of the selection + operator(colon). The following example will only look at the + arguments named p (do note that, in + general, requests can contain multiple arguments with the same name): + SecRule ARGS:p dirty + It is also possible to specify exclusions. The following will examine + all request arguments for the word dirty, except + the ones named z (again, there can be + zero or more arguments named z): + SecRule ARGS|!ARGS:z dirty + There is a special operator that allows you to count how many variables + there are in a collection. The following rule will trigger if there is + more than zero arguments in the request (ignore the second parameter for + the time being): SecRule &ARGS !^0$ + And sometimes you need to look at an array of parameters, each with a + slightly different name. In this case you can specify a regular + expression in the selection operator itself. The following rule will + look into all arguments whose names begin with id_: SecRule ARGS:/^id_/ dirty + + + Using ARGS:p will not result in any + invocations against the operator if argument p does not exist. + + In ModSecurity 1.X, the ARGS variable stood + for QUERY_STRING + POST_PAYLOAD, + whereas now it expands to individual variables. + +
+ +
+ <literal moreinfo="none">ARGS_COMBINED_SIZE</literal> + + This variable allows you to set more targeted evaluations on the + total size of the Arguments as compared with normal Apache LimitRequest + directives. For example, you could create a rule to ensure that the + total size of the argument data is below a certain threshold (to help + prevent buffer overflow issues). Example: Block request if the size of + the arguments is above 25 characters. + + SecRule REQUEST_FILENAME "^/cgi-bin/login\.php" \ + "chain,log,deny,phase:2,t:none,t:lowercase,t:normalisePath" +SecRule ARGS_COMBINED_SIZE "@gt 25" +
+ +
+ <literal moreinfo="none">ARGS_NAMES</literal> + + Is a collection of the argument names. You can search for specific + argument names that you want to block. In a positive policy scenario, + you can also whitelist (using an inverted rule with the ! character) + only authorized argument names. Example: This example rule will only + allow 2 argument names - p and a. If any other argument names are + injected, it will be blocked. + + SecRule REQUEST_FILENAME "/index.php" \ + "chain,log,deny,status:403,phase:2,t:none,t:lowercase,t:normalisePath" +SecRule ARGS_NAMES "!^(p|a)$" "t:none,t:lowercase" +
+ +
+ <literal moreinfo="none">ARGS_GET</literal> + + ARGS_GET is similar to ARGS, + but only contains arguments from the query string. +
+ +
+ <literal moreinfo="none">ARGS_GET_NAMES</literal> + + ARGS_GET_NAMES is similar to + ARGS_NAMES, but only contains argument names from the + query string. +
+ +
+ <literal moreinfo="none">ARGS_POST</literal> + + ARGS_POST is similar to + ARGS, but only contains arguments from the POST + body. +
+ +
+ <literal moreinfo="none">ARGS_POST_NAMES</literal> + + ARGS_POST_NAMES is similar to + ARGS_NAMES, but only contains argument names from the + POST body. +
+ +
+ <literal moreinfo="none">AUTH_TYPE</literal> + + This variable holds the authentication method used to validate a + user. Example: + + SecRule AUTH_TYPE "basic" log,deny,status:403,phase:1,t:lowercase + + Note + + This data will not be available in a proxy-mode deployment as the + authentication is not local. In a proxy-mode deployment, you would need + to inspect the REQUEST_HEADERS:Authorization + header. +
+ +
+ <literal moreinfo="none">ENV</literal> + + Collection, requires a single parameter (after colon). The + ENV variable is set with setenv and does not give + access to the CGI environment variables. Example: + + SecRule REQUEST_FILENAME "printenv" pass,setenv:tag=suspicious +SecRule ENV:tag "suspicious" +
+ +
+ <literal moreinfo="none">FILES</literal> + + Collection. Contains a collection of original file names (as they + were called on the remote user's file system). Note: only available if + files were extracted from the request body. Example: + + SecRule FILES "\.conf$" log,deny,status:403,phase:2 +
+ +
+ <literal moreinfo="none">FILES_COMBINED_SIZE</literal> + + Single value. Total size of the uploaded files. Note: only + available if files were extracted from the request body. Example: + + SecRule FILES_COMBINED_SIZE "@gt 1000" log,deny,status:403,phase:2 +
+ +
+ <literal moreinfo="none">FILES_NAMES</literal> + + Collection w/o parameter. Contains a list of form fields that were + used for file upload. Note: only available if files were extracted from + the request body. Example: + + SecRule FILES_NAMES "^upfile$" log,deny,status:403,phase:2 +
+ +
+ <literal moreinfo="none">FILES_SIZES</literal> + + Collection. Contains a list of file sizes. Useful for implementing + a size limitation on individual uploaded files. Note: only available if + files were extracted from the request body. Example: + + SecRule FILES_SIZES "@gt 100" log,deny,status:403,phase:2 +
+ +
+ <literal moreinfo="none">FILES_TMPNAMES</literal> + + Collection. Contains a collection of temporary files' names on the + disk. Useful when used together with @inspectFile. Note: only available if files + were extracted from the request body. Example: + + SecRule FILES_TMPNAMES "@inspectFile /path/to/inspect_script.pl" +
+ +
+ <literal moreinfo="none">GEO</literal> + + GEO is a collection populated by the results of + the last @geoLookup operator. The + collection can be used to match geographical fields looked from an IP + address or hostname. + + Available since ModSecurity 2.5.0. + + Fields: + + + + COUNTRY_CODE: Two character country code. + EX: US, GB, etc. + + + + COUNTRY_CODE3: Up to three character + country code. + + + + COUNTRY_NAME: The full country + name. + + + + COUNTRY_CONTINENT: The two character + continent that the country is located. EX: EU + + + + REGION: The two character region. For US, + this is state. For Canada, providence, etc. + + + + CITY: The city name if supported by the + database. + + + + POSTAL_CODE: The postal code if supported + by the database. + + + + LATITUDE: The latitude if supported by + the database. + + + + LONGITUDE: The longitude if supported by + the database. + + + + DMA_CODE: The metropolitan area code if + supported by the database. (US only) + + + + AREA_CODE: The phone system area code. + (US only) + + + + Example: + + SecGeoLookupDb /usr/local/geo/data/GeoLiteCity.dat +... +SecRule REMOTE_ADDR "@geoLookup" "chain,drop,msg:'Non-GB IP address'" +SecRule GEO:COUNTRY_CODE "!@streq GB" +
+ +
+ <literal moreinfo="none">HIGHEST_SEVERITY</literal> + + This variable holds the highest severity of any rules that have + matched so far. Severities are numeric values and thus can be used with + comparison operators such as @lt, + etc. + + + Higher severities have a lower numeric value. + + A value of 255 indicates no severity has been set. + + + SecRule HIGHEST_SEVERITY "@le 2" "phase:2,deny,status:500,msg:'severity %{HIGHEST_SEVERITY}'" +
+ +
+ <literal moreinfo="none">MATCHED_VAR</literal> + + This variable holds the value of the variable that was matched + against. It is similar to the TX:0, except it can be used for all + operators and does not require that the capture action be specified. + + SecRule ARGS pattern chain,deny +... +SecRule MATCHED_VAR "further scrutiny" +
+ +
+ <literal moreinfo="none">MATCHED_VAR_NAME</literal> + + This variable holds the full name of the variable that was matched + against. + + SecRule ARGS pattern setvar:tx.mymatch=%{MATCHED_VAR_NAME} +... +SecRule TX:MYMATCH "@eq ARGS:param" deny +
+ +
+ <literal moreinfo="none">MODSEC_BUILD</literal> + + This variable holds the ModSecurity build number. This variable is + intended to be used to check the build number prior to using a feature + that is available only in a certain build. Example: + + SecRule MODSEC_BUILD "!@ge 02050102" skipAfter:12345 +SecRule ARGS "@pm some key words" id:12345,deny,status:500 +
+ +
+ <literal>MULTIPART_CRLF_LF_LINES</literal> + + This flag variable will be set to 1 whenever a + multi-part request uses mixed line terminators. The + multipart/form-data RFC requires + CRLF sequence to be used to terminate lines. Since + some client implementations use only LF to terminate + lines you might want to allow them to proceed under certain + circumstances (if you want to do this you will need to stop using + MULTIPART_STRICT_ERROR and check each multi-part flag + variable individually, avoiding MULTIPART_LF_LINE). + However, mixing CRLF and LF line + terminators is dangerous as it can allow for evasion. Therefore, in such + cases, you will have to add a check for + MULTIPART_CRLF_LF_LINES. +
+ +
+ <literal>MULTIPART_STRICT_ERROR</literal> + + MULTIPART_STRICT_ERROR will be set to + 1 when any of the following variables is also set to + 1: REQBODY_PROCESSOR_ERROR, + MULTIPART_BOUNDARY_QUOTED, + MULTIPART_BOUNDARY_WHITESPACE, + MULTIPART_DATA_BEFORE, + MULTIPART_DATA_AFTER, + MULTIPART_HEADER_FOLDING, + MULTIPART_LF_LINE, + MULTIPART_SEMICOLON_MISSING + MULTIPART_INVALID_QUOTING + MULTIPART_INVALID_HEADER_FOLDING + MULTIPART_FILE_LIMIT_EXCEEDED. Each of these + variables covers one unusual (although sometimes legal) aspect of the + request body in multipart/form-data format. Your + policies should always contain a rule to check + either this variable (easier) or one or more individual variables (if + you know exactly what you want to accomplish). Depending on the rate of + false positives and your default policy you should decide whether to + block or just warn when the rule is triggered. + + The best way to use this variable is as in the example + below: + + SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ +"phase:2,t:none,log,deny,msg:'Multipart request body \ +failed strict validation: \ +PE %{REQBODY_PROCESSOR_ERROR}, \ +BQ %{MULTIPART_BOUNDARY_QUOTED}, \ +BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ +DB %{MULTIPART_DATA_BEFORE}, \ +DA %{MULTIPART_DATA_AFTER}, \ +HF %{MULTIPART_HEADER_FOLDING}, \ +LF %{MULTIPART_LF_LINE}, \ +SM %{MULTIPART_SEMICOLON_MISSING}, \ +IQ %{MULTIPART_INVALID_QUOTING}, \ +IQ %{MULTIPART_INVALID_HEADER_FOLDING}, \ +FE %{MULTIPART_FILE_LIMIT_EXCEEDED}'" + + The multipart/form-data parser was upgraded in + ModSecurity v2.1.3 to actively look for signs of evasion. Many variables + (as listed above) were added to expose various facts discovered during + the parsing process. The MULTIPART_STRICT_ERROR + variable is handy to check on all abnormalities at once. The individual + variables allow detection to be fine-tuned according to your + circumstances in order to reduce the number of false positives. Detailed + analysis of various evasion techniques covered will be released as a + separated document at a later date. +
+ +
+ <literal>MULTIPART_UNMATCHED_BOUNDARY</literal> + + Set to 1 when, during the parsing phase of a + multipart/request-body, ModSecurity encounters what + feels like a boundary but it is not. Such an event may occur when + evasion of ModSecurity is attempted. + + The best way to use this variable is as in the example + below: + + SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \ +"phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'" + + Change the rule from blocking to logging-only if many false + positives are encountered. +
+ +
+ <literal moreinfo="none">PATH_INFO</literal> + + Besides passing query information to a script/handler, you can + also pass additional data, known as extra path information, as part of + the URL. Example: + + SecRule PATH_INFO "^/(bin|etc|sbin|opt|usr)" +
+ +
+ <literal moreinfo="none">QUERY_STRING</literal> + + This variable holds form data passed to the script/handler by + appending data after a question mark. Warning: Not URL-decoded. + Example: + + SecRule QUERY_STRING "attack" +
+ +
+ <literal moreinfo="none">REMOTE_ADDR</literal> + + This variable holds the IP address of the remote client. + Example: + + SecRule REMOTE_ADDR "^192\.168\.1\.101$" +
+ +
+ <literal moreinfo="none">REMOTE_HOST</literal> + + If HostnameLookUps are set to On, then this variable will hold the + DNS resolved remote host name. If it is set to Off, then it will hold + the remote IP address. Possible uses for this variable would be to deny + known bad client hosts or network blocks, or conversely, to allow in + authorized hosts. Example: + + SecRule REMOTE_HOST "\.evil\.network\org$" +
+ +
+ <literal moreinfo="none">REMOTE_PORT</literal> + + This variable holds information on the source port that the client + used when initiating the connection to our web server. Example: in this + example, we are evaluating to see if the REMOTE_PORT + is less than 1024, which would indicate that the user is a privileged + user (root). + + SecRule REMOTE_PORT "@lt 1024" phase:1,log,pass,setenv:remote_port=privileged +
+ +
+ <literal moreinfo="none">REMOTE_USER</literal> + + This variable holds the username of the authenticated user. If + there are no password (basic|digest) access controls in place, then this + variable will be empty. Example: + + SecRule REMOTE_USER "admin" + + Note + + This data will not be available in a proxy-mode deployment as the + authentication is not local. +
+ +
+ <literal moreinfo="none">REQBODY_PROCESSOR</literal> + + Built-in processors are URLENCODED, + MULTIPART, and XML. + Example: + + SecRule REQBODY_PROCESSOR "^XML$ chain +SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" +
+ +
+ <literal + moreinfo="none">REQBODY_PROCESSOR_ERROR</literal> + + Possible values are 0 (no error) or 1 (error). This variable will + be set by request body processors (typically the + multipart/request-data parser or the XML parser) + when they fail to properly parse a request payload. + + Example: + + SecRule REQBODY_PROCESSOR_ERROR "@eq 1" deny,phase:2 + + + Your policies must have a rule to check + REQBODY_PROCESSOR_ERROR at the beginning of phase 2. Failure to do so + will leave the door open for impedance mismatch attacks. It is + possible, for example, that a payload that cannot be parsed by + ModSecurity can be successfully parsed by more tolerant parser + operating in the application. If your policy dictates blocking then + you should reject the request if error is detected. When operating in + detection-only mode your rule should alert with high severity when + request body processing fails. + +
+ +
+ <literal + moreinfo="none">REQBODY_PROCESSOR_ERROR_MSG</literal> + + Empty, or contains the error message from the processor. + Example: + + SecRule REQBODY_PROCESSOR_ERROR_MSG "failed to parse" t:lowercase +
+ +
+ <literal moreinfo="none">REQUEST_BASENAME</literal> + + This variable holds just the filename part of + REQUEST_FILENAME (e.g. index.php). + + Example: + + SecRule REQUEST_BASENAME "^login\.php$" phase:2,t:none,t:lowercase + + + Please note that anti-evasion transformations are not applied to + this variable by default. REQUEST_BASENAME will + recognise both / and \ as path + separators. + +
+ +
+ <literal moreinfo="none">REQUEST_BODY</literal> + + This variable holds the data in the request body (including + POST_PAYLOAD data). REQUEST_BODY + should be used if the original order of the arguments is important + (ARGS should be used in all other cases). + Example: + + SecRule REQUEST_BODY "^username=\w{25,}\&password=\w{25,}\&Submit\=login$" + + + This variable is only available if the + URLENCODED request body processor parsed a request + body. This will occur by default when an + application/x-www-form-urlencoded is detected, or + the URLENCODED request body parser is forced. As of + 2.5.7 it is possible to force the presence of the + REQUEST_BODY variable, but only when there is no + request body processor defined, using the + ctl:forceRequestBodyVariable option in the + REQUEST_HEADERS phase. + +
+ +
+ <literal moreinfo="none">REQUEST_COOKIES</literal> + + This variable is a collection of all of the cookie data. Example: + the following example is using the Ampersand special operator to count + how many variables are in the collection. In this rule, it would trigger + if the request does not include any Cookie headers. + + SecRule &REQUEST_COOKIES "@eq 0" +
+ +
+ <literal moreinfo="none">REQUEST_COOKIES_NAMES</literal> + + This variable is a collection of the cookie names in the request + headers. Example: the following rule will trigger if the JSESSIONID + cookie is not present. + + SecRule &REQUEST_COOKIES_NAMES:JSESSIONID "@eq 0" +
+ +
+ <literal moreinfo="none">REQUEST_FILENAME</literal> + + This variable holds the relative REQUEST_URI + minus the QUERY_STRING part (e.g. /index.php). + Example: + + SecRule REQUEST_FILENAME "^/cgi-bin/login\.php$" phase:2,t:none,t:normalisePath + + + Please note that anti-evasion transformations are not used on + REQUEST_FILENAME by default. + +
+ +
+ <literal moreinfo="none">REQUEST_HEADERS</literal> + + This variable can be used as either a collection of all of the + request headers or can be used to specify individual headers (by using + REQUEST_HEADERS:Header-Name). Example: the first + example uses REQUEST_HEADERS as a collection and is + applying the validateUrlEncoding operator against all + headers. + + SecRule REQUEST_HEADERS "@validateUrlEncoding" + + Example: the second example is targeting only the + Host header. + + SecRule REQUEST_HEADERS:Host "^[\d\.]+$" \ + "deny,log,status:400,msg:'Host header is a numeric IP address'" +
+ +
+ <literal moreinfo="none">REQUEST_HEADERS_NAMES</literal> + + This variable is a collection of the names of all of the request + headers. Example: + + SecRule REQUEST_HEADERS_NAMES "^x-forwarded-for" \ + "log,deny,status:403,t:lowercase,msg:'Proxy Server Used'" +
+ +
+ <literal moreinfo="none">REQUEST_LINE</literal> + + This variable holds the complete request line sent to the server + (including the REQUEST_METHOD and HTTP version data). Example: this + example rule will trigger if the request method is something other than + GET, HEAD, POST or if the HTTP is something other than HTTP/0.9, 1.0 or + 1.1. + + SecRule REQUEST_LINE "!(^((?:(?:pos|ge)t|head))|http/(0\.9|1\.0|1\.1)$)" t:none,t:lowercase +
+ +
+ <literal moreinfo="none">REQUEST_METHOD</literal> + + This variable holds the request method used by the client. + + The following example will trigger if the request method is either + CONNECT or TRACE. + + SecRule REQUEST_METHOD "^((?:connect|trace))$" t:none,t:lowercase +
+ +
+ <literal moreinfo="none">REQUEST_PROTOCOL</literal> + + This variable holds the request protocol version information. + Example: + + SecRule REQUEST_PROTOCOL "!^http/(0\.9|1\.0|1\.1)$" t:none,t:lowercase +
+ +
+ <literal moreinfo="none">REQUEST_URI</literal> + + This variable holds the full URL including the + QUERY_STRING data (e.g. /index.php?p=X), however it + will never contain a domain name, even if it was provided on the request + line. It also does not include either the + REQUEST_METHOD or the HTTP version info. + + Example: + + SecRule REQUEST_URI "attack" phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath + + + Please note that anti-evasion transformations are not used on + REQUEST_URI by default. + +
+ +
+ <literal moreinfo="none">REQUEST_URI_RAW</literal> + + Same as REQUEST_URI but will contain the domain + name if it was provided on the request line (e.g. + http://www.example.com/index.php?p=X). + + Example: + + SecRule REQUEST_URI_RAW "http:/" phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath + + + Please note that anti-evasion transformations are not used on + REQUEST_URI_RAW by default. + +
+ +
+ <literal moreinfo="none">RESPONSE_BODY</literal> + + This variable holds the data for the response payload. + + Example: + + SecRule RESPONSE_BODY "ODBC Error Code" +
+ +
+ <literal>RESPONSE_CONTENT_LENGTH</literal> + + Response body length in bytes. Can be available starting with + phase 3 but it does not have to be (as the length of response body is + not always known in advance.) If the size is not known this variable + will contain a zero. If RESPONSE_CONTENT_LENGTH + contains a zero in phase 5 that means the actual size of the response + body was 0. + + The value of this variable can change between phases if the body + is modified. For example, in embedded mode + mod_deflate can compress the response body between + phases 4 and 5. +
+ +
+ <literal>RESPONSE_CONTENT_TYPE</literal> + + Response content type. Only available starting with phase + 3. +
+ +
+ <literal moreinfo="none">RESPONSE_HEADERS</literal> + + This variable is similar to the REQUEST_HEADERS variable and can + be used in the same manner. Example: + + SecRule RESPONSE_HEADERS:X-Cache "MISS" + + Note + + This variable may not have access to some headers when running in + embedded-mode. Headers such as Server, Date, Connection and Content-Type + are added during a later Apache hook just prior to sending the data to + the client. This data should be available, however, either during + ModSecurity phase:5 (logging) or when running in proxy-mode. +
+ +
+ <literal moreinfo="none">RESPONSE_HEADERS_NAMES</literal> + + This variable is a collection of the response header names. + Example: + + SecRule RESPONSE_HEADERS_NAMES "Set-Cookie" + + Note + + Same limitations as RESPONSE_HEADERS with regards to access to + some headers in embedded-mode. +
+ +
+ <literal moreinfo="none">RESPONSE_PROTOCOL</literal> + + This variable holds the HTTP response protocol information. + Example: + + SecRule RESPONSE_PROTOCOL "^HTTP\/0\.9" +
+ +
+ <literal moreinfo="none">RESPONSE_STATUS</literal> + + This variable holds the HTTP response status code as generated by + Apache. Example: + + SecRule RESPONSE_STATUS "^[45]" + + Note + + This directive may not work as expected in embedded-mode as Apache + handles many of the stock response codes (404, 401, etc...) earlier in + Phase 2. This variable should work as expected in a proxy-mode + deployment. +
+ +
+ <literal moreinfo="none">RULE</literal> + + This variable provides access to the id, rev, + severity, logdata, and msg fields of the rule that triggered the + action. Only available for expansion in action strings (e.g.setvar:tx.varname=%{rule.id}). Example: + + SecRule &REQUEST_HEADERS:Host "@eq 0" "log,deny,setvar:tx.varname=%{rule.id}" +
+ +
+ <literal moreinfo="none">SCRIPT_BASENAME</literal> + + This variable holds just the local filename part of + SCRIPT_FILENAME. Example: + + SecRule SCRIPT_BASENAME "^login\.php$" + + Note + + This variable is not available in proxy mode. +
+ +
+ <literal moreinfo="none">SCRIPT_FILENAME</literal> + + This variable holds the full path on the server to the requested + script. (e.g. SCRIPT_NAME plus the server path). Example: + + SecRule SCRIPT_FILENAME "^/usr/local/apache/cgi-bin/login\.php$" + + Note + + This variable is not available in proxy mode. +
+ +
+ <literal moreinfo="none">SCRIPT_GID</literal> + + This variable holds the group id (numerical value) of the group + owner of the script. Example: + + SecRule SCRIPT_GID "!^46$" + + Note + + This variable is not available in proxy mode. +
+ +
+ <literal moreinfo="none">SCRIPT_GROUPNAME</literal> + + This variable holds the group name of the group owner of the + script. Example: + + SecRule SCRIPT_GROUPNAME "!^apache$" + + Note + + This variable is not available in proxy mode. +
+ +
+ <literal moreinfo="none">SCRIPT_MODE</literal> + + This variable holds the script's permissions mode data (numerical + - 1=execute, 2=write, 4=read and 7=read/write/execute). Example: will + trigger if the script has the WRITE permissions set. + + SecRule SCRIPT_MODE "^(2|3|6|7)$" + + Note + + This variable is not available in proxy mode. +
+ +
+ <literal moreinfo="none">SCRIPT_UID</literal> + + This variable holds the user id (numerical value) of the owner of + the script. Example: the example rule below will trigger if the UID is + not 46 (the Apache user). + + SecRule SCRIPT_UID "!^46$" + + Note + + This variable is not available in proxy mode. +
+ +
+ <literal moreinfo="none">SCRIPT_USERNAME</literal> + + This variable holds the username of the owner of the script. + Example: + + SecRule SCRIPT_USERNAME "!^apache$" + + Note + + This variable is not available in proxy mode. +
+ +
+ <literal moreinfo="none">SERVER_ADDR</literal> + + This variable contains the IP address of the server. + Example: + + SecRule SERVER_ADDR "^192\.168\.1\.100$" +
+ +
+ <literal moreinfo="none">SERVER_NAME</literal> + + This variable contains the server's hostname or IP address. + Example: + + SecRule SERVER_NAME "hostname\.com$" + + Note + + This data is taken from the Host header submitted in the client + request. +
+ +
+ <literal moreinfo="none">SERVER_PORT</literal> + + This variable contains the local port that the web server is + listening on. Example: + + SecRule SERVER_PORT "^80$" +
+ +
+ <literal moreinfo="none">SESSION</literal> + + This variable is a collection, available only after setsid is executed. Example: the following + example shows how to initialize a SESSION collection with setsid, how to + use setvar to increase the session.score values, how to set the + session.blocked variable and finally how to deny the connection based on + the session:blocked value. + + SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass +SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} +SecRule REQUEST_URI "^/cgi-bin/finger$" \ + "phase:2,t:none,t:lowercase,t:normalisePath,pass,log,setvar:session.score=+10" +SecRule SESSION:SCORE "@gt 50" "pass,log,setvar:session.blocked=1" +SecRule SESSION:BLOCKED "@eq 1" "log,deny,status:403" +
+ +
+ <literal moreinfo="none">SESSIONID</literal> + + This variable is the value set with setsid. Example: + + SecRule SESSIONID !^$ chain,nolog,pass +SecRule REQUEST_COOKIES:PHPSESSID !^$ +SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} +
+ +
+ <literal moreinfo="none">TIME</literal> + + This variable holds a formatted string representing the time + (hour:minute:second). Example: + + SecRule TIME "^(([1](8|9))|([2](0|1|2|3))):\d{2}:\d{2}$" +
+ +
+ <literal moreinfo="none">TIME_DAY</literal> + + This variable holds the current date (1-31). Example: this rule + would trigger anytime between the 10th and 20th days of the + month. + + SecRule TIME_DAY "^(([1](0|1|2|3|4|5|6|7|8|9))|20)$" +
+ +
+ <literal moreinfo="none">TIME_EPOCH</literal> + + This variable holds the time in seconds since 1970. + Example: + + SecRule TIME_EPOCH "@gt 1000" +
+ +
+ <literal moreinfo="none">TIME_HOUR</literal> + + This variable holds the current hour (0-23). Example: this rule + would trigger during "off hours". + + SecRule TIME_HOUR "^(0|1|2|3|4|5|6|[1](8|9)|[2](0|1|2|3))$" +
+ +
+ <literal moreinfo="none">TIME_MIN</literal> + + This variable holds the current minute (0-59). Example: this rule + would trigger during the last half hour of every hour. + + SecRule TIME_MIN "^(3|4|5)" +
+ +
+ <literal moreinfo="none">TIME_MON</literal> + + This variable holds the current month (0-11). Example: this rule + would match if the month was either November (10) or December + (11). + + SecRule TIME_MON "^1" +
+ +
+ <literal moreinfo="none">TIME_SEC</literal> + + This variable holds the current second count (0-59). + Example: + + SecRule TIME_SEC "@gt 30" +
+ +
+ <literal moreinfo="none">TIME_WDAY</literal> + + This variable holds the current weekday (0-6). Example: this rule + would trigger only on week-ends (Saturday and Sunday). + + SecRule TIME_WDAY "^(0|6)$" +
+ +
+ <literal moreinfo="none">TIME_YEAR</literal> + + This variable holds the current four-digit year data. + Example: + + SecRule TIME_YEAR "^2006$" +
+ +
+ <literal moreinfo="none">TX</literal> + + Transaction Collection. This is used to store pieces of data, + create a transaction anomaly score, and so on. Transaction variables are + set for 1 request/response cycle. The scoring and evaluation will not + last past the current request/response process. Example: In this + example, we are using setvar to increase the tx.score value by 5 points. + We then have a follow-up run that will evaluate the transactional score + this request and then it will decided whether or not to allow/deny the + request through. + + The following is a list of reserved names in the TX + collection: + + + + TX:0 - The matching value + when using the @rx or @pm operator with the capture action. + + + + TX:1-TX:9 - The captured + subexpression value when using the @rx operator with capturing parens and the + capture action. + + + + TX:MSC_* - ModSecurity + processing flags. + + + + MSC_PCRE_LIMITS_EXCEEDED - Set + non-zero if PCRE match limits are exceeded. See SecPcreMatchLimit and SecPcreMatchLimitRecursion. + + + + + + SecRule WEBSERVER_ERROR_LOG "does not exist" "phase:5,pass,setvar:tx.score=+5" +SecRule TX:SCORE "@gt 20" deny,log +
+ +
+ <literal moreinfo="none">USERID</literal> + + This variable is the value set with setuid. Example: + + SecAction setuid:%{REMOTE_USER},nolog +SecRule USERID "Admin" +
+ +
+ <literal moreinfo="none">WEBAPPID</literal> + + This variable is the value set with SecWebAppId. Example: + + SecWebAppId "WebApp1" +SecRule WEBAPPID "WebApp1" "chain,log,deny,status:403" +SecRule REQUEST_HEADERS:Transfer-Encoding "!^$" +
+ +
+ <literal moreinfo="none">WEBSERVER_ERROR_LOG</literal> + + Contains zero or more error messages produced by the web server. + Access to this variable is in phase:5 (logging). Example: + + SecRule WEBSERVER_ERROR_LOG "File does not exist" "phase:5,setvar:tx.score=+5" +
+ +
+ <literal moreinfo="none">XML</literal> + + Can be used standalone (as a target for + validateDTD and validateSchema) or + with an XPath expression parameter (which makes it a valid target for + any function that accepts plain text). Example using XPath: + + SecDefaultAction log,deny,status:403,phase:2 +SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ + phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML +SecRule REQBODY_PROCESSOR "!^XML$" skipAfter:12345 +SecRule XML:/employees/employee/name/text() Fred +SecRule XML:/xq:employees/employee/name/text() Fred \ + id:12345,xmlns:xq=http://www.example.com/employees + + The first XPath expression does not use namespaces. It would match + against payload such as this one: + + <employees> + <employee> + <name>Fred Jones</name> + <address location="home"> + <street>900 Aurora Ave.</street> + <city>Seattle</city> + <state>WA</state> + <zip>98115</zip> + </address> + <address location="work"> + <street>2011 152nd Avenue NE</street> + <city>Redmond</city> + <state>WA</state> + <zip>98052</zip> + </address> + <phone location="work">(425)555-5665</phone> + <phone location="home">(206)555-5555</phone> + <phone location="mobile">(206)555-4321</phone> + </employee> +</employees> + + The second XPath expression does use namespaces. It would match + the following payload: + + <xq:employees xmlns:xq="http://www.example.com/employees"> + <employee> + <name>Fred Jones</name> + <address location="home"> + <street>900 Aurora Ave.</street> + <city>Seattle</city> + <state>WA</state> + <zip>98115</zip> + </address> + <address location="work"> + <street>2011 152nd Avenue NE</street> + <city>Redmond</city> + <state>WA</state> + <zip>98052</zip> + </address> + <phone location="work">(425)555-5665</phone> + <phone location="home">(206)555-5555</phone> + <phone location="mobile">(206)555-4321</phone> + </employee> +</xq:employees> + + Note the different namespace used in the second example. + + To learn more about XPath we suggest the following + resources: + + + + XPath + Standard + + + + XPath + Tutorial + + +
+
+ +
+ Transformation functions + + When ModSecurity receives request or response information, it makes + a copy of this data and places it into memory. It is on this data in + memory that transformation functions are applied. The raw request/response + data is never altered. Transformation functions are used to transform a + variable before testing it in a rule. + + Note + + There are no default transformation functions as there were in + previous versions of ModSecurity. + + The following rule will ensure that an attacker does not use mixed + case in order to evade the ModSecurity rule: + + SecRule ARGS:p "xp_cmdshell" "t:lowercase" + multiple transformation actions can be used in the same rule, for example + the following rule also ensures that an attacker does not use URL encoding + (%xx encoding) for evasion. Note the order of the transformation + functions, which ensures that a URL encoded letter is first decoded and + than translated to lower case. + + SecRule ARGS:p "xp_cmdshell" "t:urlDecode,t:lowercase" + + One can use the SecDefaultAction command to ensure the translation + occurs for every rule until the next. Note that transformation actions are + additive, so if a rule explicitly list actions, the translation actions + set by SecDefaultAction are still performed. + + SecDefaultAction t:urlDecode,t:lowercase + + The following transformation functions are supported: + +
+ <literal>base64Decode</literal> + + This function decodes a base64-encoded string. +
+ +
+ <literal>base64Encode</literal> + + This function encodes input string using base64 encoding. +
+ +
+ <literal>compressWhitespace</literal> + + It converts whitespace characters (32, \f, \t, \n, \r, \v, 160) to + spaces (ASCII 32) and then compresses multiple consecutive space + characters into one. +
+ +
+ cssDecode + + Decodes CSS-encoded characters, as specified at http://www.w3.org/TR/REC-CSS2/syndata.html. + This function uses only up to two bytes in the decoding process, meaning + it is useful to uncover ASCII characters (that wouldn't normally be + encoded) encoded using CSS encoding, or to counter evasion which is a + combination of a backslash and non-hexadecimal characters (e.g. + ja\vascript is equivalent to + javascript). +
+ +
+ <literal>escapeSeqDecode</literal> + + This function decode ANSI C escape sequences: \a, \b, + \f, \n, \r, + \t, \v, \\, + \?, \', \", + \xHH (hexadecimal), \0OOO (octal). Invalid encodings are left in + the output. +
+ +
+ <literal>hexDecode</literal> + + This function decodes a hex-encoded string. +
+ +
+ <literal>hexEncode</literal> + + This function encodes input as hex-encoded string. +
+ +
+ <literal>htmlEntityDecode</literal> + + This function decodes HTML entities present in input. The + following variants are supported: + + + + &#xHH and &#xHH; (where H is any hexadecimal + number) + + + + &#DDD and &#DDD; (where D is any decimal + number) + + + + &quot and &quot; + + + + &nbsp and &nbsp; + + + + &lt and &lt; + + + + &gt and &gt; + + + + This function will convert any entity into a single byte only, + possibly resulting in a loss of information. It is thus useful to + uncover bytes that would otherwise not need to be encoded, but it cannot + do anything with the characters from the range above 255. +
+ +
+ <literal>jsDecode</literal> + + Decodes JavaScript escape sequences. If a + \uHHHH code is in the range of + FF01-FF5E (the full width ASCII + codes), then the higher byte is used to detect and adjust the lower + byte. Otherwise, only the lower byte will be used and the higher byte + zeroed. +
+ +
+ <literal>length</literal> + + This function converts the input to its numeric length (count of + bytes). +
+ +
+ <literal>lowercase</literal> + + This function converts all characters to lowercase using the + current C locale. +
+ +
+ <literal>md5</literal> + + This function calculates an MD5 hash from input. Note that the + computed hash is in a raw binary form and may need encoded into text to + be usable (for example: t:md5,t:hexEncode). +
+ +
+ <literal><literal>none</literal></literal> + + Not an actual transformation function, but an instruction to + ModSecurity to remove all transformation functions associated with the + current rule. +
+ +
+ <literal>normalisePath</literal> + + This function will remove multiple slashes, self-references and + directory back-references (except when they are at the beginning of the + input). +
+ +
+ <literal>normalisePathWin</literal> + + Same as normalisePath, but will first convert + backslash characters to forward slashes. +
+ +
+ <literal>parityEven7bit</literal> + + This function calculates even parity of 7-bit data replacing the + 8th bit of each target byte with the calculated parity bit. +
+ +
+ <literal>parityOdd7bit</literal> + + This function calculates odd parity of 7-bit data replacing the + 8th bit of each target byte with the calculated parity bit. +
+ +
+ <literal>parityZero7bit</literal> + + This function calculates zero parity of 7-bit data replacing the + 8th bit of each target byte with a zero parity bit which allows + inspection of even/odd parity 7bit data as ASCII7 data. +
+ +
+ <literal>removeNulls</literal> + + This function removes NULL bytes from input. +
+ +
+ <literal>removeWhitespace</literal> + + This function removes all whitespace characters from input. +
+ +
+ <literal>replaceComments</literal> + + This function replaces each occurrence of a C-style comments + (/* ... */) with a single space + (multiple consecutive occurrences of a space will not be compressed). + Unterminated comments will too be replaced with a space (ASCII 32). + However, a standalone termination of a comment (*/) will not be acted upon. +
+ +
+ <literal>replaceNulls</literal> + + This function is enabled by default. It replaces NULL bytes in + input with spaces (ASCII 32). +
+ +
+ <literal>urlDecode</literal> + + This function decodes an URL-encoded input string. Invalid + encodings (i.e. the ones that use non-hexadecimal characters, or the + ones that are at the end of string and have one or two characters + missing) will not be converted. If you want to detect invalid encodings + use the @validateUrlEncoding + operator. The transformation function should not be used against + variables that have already been URL-decoded unless it is your intention + to perform URL decoding twice! +
+ +
+ <literal>urlDecodeUni</literal> + + In addition to decoding %xx like urlDecode, urlDecodeUni also decodes %uXXXX encoding. If the code is in the range + of FF01-FF5E (the full width ASCII + codes), then the higher byte is used to detect and adjust the lower + byte. Otherwise, only the lower byte will be used and the higher byte + zeroed. +
+ +
+ <literal>urlEncode</literal> + + This function encodes input using URL encoding. +
+ +
+ <literal>sha1</literal> + + This function calculates a SHA1 hash from input. Note that the + computed hash is in a raw binary form and may need encoded to be usable + (for example: t:sha1,t:hexEncode). +
+ +
+ <literal>trimLeft</literal> + + This function removes whitespace from the left side of + input. +
+ +
+ <literal>trimRight</literal> + + This function removes whitespace from the right side of + input. +
+ +
+ <literal>trim</literal> + + This function removes whitespace from both the left and right + sides of input. +
+
+ +
+ Actions + + Each action belongs to one of five groups: + + + + Disruptive actions + + + Cause ModSecurity to do something. In many cases something + means block transaction, but not in all. For example, the allow + action is classified as a disruptive action, but it does the + opposite of blocking. There can only be one disruptive action per + rule (if there are multiple disruptive actions present, or + inherited, only the last one will take effect), or rule chain (in a + chain, a disruptive action can only appear in the first + rule). + + + + + Non-disruptive actions + + + Do something, but that something does not and cannot affect + the rule processing flow. Setting a variable, or changing its value + is an example of a non-disruptive action. Non-disruptive action can + appear in any rule, including each rule belonging to a chain. + + + + + Flow actions + + + These actions affect the rule flow (for example + skip or skipAfter). + + + + + Meta-data actions + + + Meta-data actions are used to provide more information about + rules. Examples include id, + rev, severity and + msg. + + + + + Data actions + + + Not really actions, these are mere containers that hold data + used by other actions. For example, the status + action holds the status that will be used for blocking (if it takes + place). + + + + +
+ <literal>allow</literal> + + Description: Stops rule processing on a + successful match and allows the transaction to proceed. + + Action Group: Disruptive + + Example: + + SecRule REMOTE_ADDR "^192\.168\.1\.100$" nolog,phase:1,allow + + Prior to ModSecurity 2.5 the allow action would + only affect the current phase. An allow in phase 1 + would skip processing the remaining rules in phase 1 but the rules from + phase 2 would execute. Starting with v2.5.0 allow was + enhanced to allow for fine-grained control of what is done. The + following rules now apply: + + + + If used one its own, like in the example above, + allow will affect the entire transaction, + stopping processing of the current phase but also skipping over all + other phases apart from the logging phase. (The logging phase is + special; it is designed to always execute.) + + + + If used with parameter "phase", allow will + cause the engine to stop processing the current phase. Other phases + will continue as normal. + + + + If used with parameter "request", allow + will cause the engine to stop processing the current phase. The next + phase to be processed will be phase + RESPONSE_HEADERS. + + + + Examples: + + # Do not process request but process response. +SecAction phase:1,allow:request + +# Do not process transaction (request and response). +SecAction phase:1,allow + + + If you want to allow a response through, put a rule in phase + RESPONSE_HEADERS and simply use + allow on its own: + + # Allow response through. +SecAction phase:3,allow +
+ +
+ append + + Description: Appends text given as parameter + to the end of response body. For this action to work content injection + must be enabled by setting SecContentInjection to + On. Also make sure you check the content type of the + response before you make changes to it (e.g. you don't want to inject + stuff into images). + + Action Group: Non-disruptive + + Processing Phases: 3 and 4. + + Example: + + SecRule RESPONSE_CONTENT_TYPE "^text/html" "nolog,pass,append:'<hr>Footer'" + + + While macro expansion is allowed in the additional content, you + are strongly cautioned against inserting user defined data + fields. + +
+ +
+ <literal>auditlog</literal> + + Description: Marks the transaction for + logging in the audit log. + + Action Group: Non-disruptive + + Example: + + SecRule REMOTE_ADDR "^192\.168\.1\.100$" auditlog,phase:1,allow + + Note + + The auditlog action is now explicit if log is already + specified. +
+ +
+ <literal>block</literal> + + Description: Performs the default disruptive + action. + + Action Group: Disruptive + + It is intended to be used by ruleset writers to signify that the + rule was intended to block and leaves the "how" up to the administrator. + This action is currently a placeholder which will just be replaced by + the action from the last SecDefaultAction in the same + context. Using the block action with the + SecRuleUpdateActionById directive allows a rule to be + reverted back to the previous SecDefaultAction + disruptive action. + + In future versions of ModSecurity, more control and functionality + will be added to define "how" to block. + + Examples: + + In the following example, the second rule will "deny" because of + the SecDefaultAction disruptive action. The intent being that the + administrator could easily change this to another disruptive action + without editing the actual rules. + + ### Administrator defines "how" to block (deny,status:403)... +SecDefaultAction phase:2,deny,status:403,log,auditlog + +### Included from a rulest... +# Intent is to warn for this User Agent +SecRule REQUEST_HEADERS:User-Agent "perl" "phase:2,pass,msg:'Perl based user agent identified'" +# Intent is to block for this User Agent, "how" described in SecDefaultAction +SecRule REQUEST_HEADERS:User-Agent "nikto" "phase:2,block,msg:'Nikto Scanners Identified'" + + In the following example, The rule is reverted back to the + pass action defined in the SecDefaultAction directive + by using the SecRuleUpdateActionById directive in + conjuction with the block action. This allows an + administrator to override an action in a 3rd party rule without + modifying the rule itself. + + ### Administrator defines "how" to block (deny,status:403)... +SecDefaultAction phase:2,pass,log,auditlog + +### Included from a rulest... +SecRule REQUEST_HEADERS:User-Agent "nikto" "id:1,phase:2,deny,msg:'Nikto Scanners Identified'" + +### Added by the administrator +SecRuleUpdateActionById 1 "block" +
+ +
+ <literal>capture</literal> + + Description: When used together with the + regular expression operator, capture action will create copies of + regular expression captures and place them into the transaction variable + collection. Up to ten captures will be copied on a successful pattern + match, each with a name consisting of a digit from 0 to 9. + + Action Group: Non-disruptive + + Example: + + SecRule REQUEST_BODY "^username=(\w{25,})" phase:2,capture,t:none,chain +SecRule TX:1 "(?:(?:a(dmin|nonymous)))" + + Note + + The 0 data captures the entire REGEX match and 1 captures the data + in the first parens, etc... +
+ +
+ <literal>chain</literal> + + Description: Chains the rule where the action + is placed with the rule that immediately follows it. The result is + called a rule chain. Chained rules allow for more + complex rule matches where you want to use a number of different + VARIABLES to create a better rule and to help prevent false + positives. + + Action Group: Flow + + Example: + + # Refuse to accept POST requests that do +# not specify request body length. Do note that +# this rule should be preceeded by a rule that verifies +# only valid request methods (e.g. GET, HEAD and POST) are used. +SecRule REQUEST_METHOD ^POST$ chain,t:none +SecRule REQUEST_HEADERS:Content-Length ^$ t:none + + + In programming language concepts, think of chained rules + somewhat similar to AND conditional statements. The actions specified + in the first portion of the chained rule will only be triggered if all + of the variable checks return positive hits. If one aspect of the + chained rule is negative, then the entire rule chain is negative. Also + note that disruptive actions, execution phases, metadata actions (id, + rev, msg), skip and skipAfter actions can only be specified on by the + chain starter rule. + +
+ +
+ <literal>ctl</literal> + + Description: The ctl action allows + configuration options to be updated for the transaction. + + Action Group: Non-disruptive + + Example: + + # Parse requests with Content-Type "text/xml" as XML +SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProcessor=XML + + Note + + The following configuration options are supported: + + + + auditEngine + + + + auditLogParts + + + + debugLogLevel + + + + ruleRemoveById (single rule + ID, or a single rule ID range accepted as parameter) + + + + requestBodyAccess + + + + forceRequestBodyVariable + + + + requestBodyLimit + + + + requestBodyProcessor + + + + responseBodyAccess + + + + responseBodyLimit + + + + ruleEngine + + + + With the exception of + requestBodyProcessor and + forceRequestBodyVariable, each configuration option + corresponds to one configuration directive and the usage is + identical. + + The requestBodyProcessor option allows you to + configure the request body processor. By default ModSecurity will use + the URLENCODED and MULTIPART processors to process an application/x-www-form-urlencoded and a + multipart/form-data bodies, + respectively. A third processor, XML, is also + supported, but it is never used implicitly. Instead you must tell + ModSecurity to use it by placing a few rules in the REQUEST_HEADERS processing phase. After the + request body was processed as XML you will be able to use the + XML-related features to inspect it. + + Request body processors will not interrupt a transaction if an + error occurs during parsing. Instead they will set variables REQBODY_PROCESSOR_ERROR and REQBODY_PROCESSOR_ERROR_MSG. These variables + should be inspected in the REQUEST_BODY phase and an appropriate action + taken. + + The forceRequestBodyVariable option allows you + to configure the REQUEST_BODY variable to be set when + there is no request body processor configured. This allows for + inspection of request bodies of unknown types. +
+ +
+ <literal>deny</literal> + + Description: Stops rule processing and + intercepts transaction. + + Action Group: Disruptive + + Example: + + SecRule REQUEST_HEADERS:User-Agent "nikto" "log,deny,msg:'Nikto Scanners Identified'" +
+ +
+ <literal>deprecatevar</literal> + + Description: Decrement counter based on its + age. + + Action Group: Non-Disruptive + + Example: The following example will decrement the counter by 60 + every 300 seconds. + + SecAction deprecatevar:session.score=60/300 + + Note + + Counter values are always positive, meaning the value will never + go below zero. +
+ +
+ <literal>drop</literal> + + Description: Immediately initiate a + "connection close" action to tear down the TCP connection by sending a + FIN packet. + + Action Group: Disruptive + + Example: The following example initiates an IP collection for + tracking Basic Authentication attempts. If the client goes over the + threshold of more than 25 attempts in 2 minutes, it will DROP subsequent + connections. + + SecAction phase:1,initcol:ip=%{REMOTE_ADDR},nolog +SecRule ARGS:login "!^$" \ + nolog,phase:1,setvar:ip.auth_attempt=+1,deprecatevar:ip.auth_attempt=20/120 +SecRule IP:AUTH_ATTEMPT "@gt 25" \ + "log,drop,phase:1,msg:'Possible Brute Force Attack'" + + Note + + This action is currently not available on Windows based builds. + This action is extremely useful when responding to both Brute Force and + Denial of Service attacks in that, in both cases, you want to minimize + both the network bandwidth and the data returned to the client. This + action causes error message to appear in the log "(9)Bad file + descriptor: core_output_filter: writing data to the network" +
+ +
+ <literal>exec</literal> + + Description: Executes an external + script/binary supplied as parameter. As of v2.5.0, if the parameter + supplied to exec is a Lua script (detected by the + .lua extension) the script will be processed + internally. This means you will get direct access + to the internal request context from the script. Please read the + SecRuleScript documentation for more details on how + to write Lua scripts. + + Action Group: Non-disruptive + + Example: + + # The following is going to execute /usr/local/apache/bin/test.sh +# as a shell script on rule match. +SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ + "phase:2,t:none,t:lowercase,t:normalisePath,log,exec:/usr/local/apache/bin/test.sh" + +# The following is going to process /usr/local/apache/conf/exec.lua +# internally as a Lua script on rule match. +SecRule ARGS:p attack log,exec:/usr/local/apache/conf/exec.lua + + + The exec action is executed independently from any disruptive + actions. External scripts will always be called with no parameters. + Some transaction information will be placed in environment variables. + All the usual CGI environment variables will be there. You should be + aware that forking a threaded process results in all threads being + replicated in the new process. Forking can therefore incur larger + overhead in multi-threaded operation. The script you execute must + write something (anything) to stdout. If it doesn't ModSecurity will + assume execution didn't work. + +
+ +
+ <literal>expirevar</literal> + + Description: Configures a collection variable + to expire after the given time in seconds. + + Action Group: Non-disruptive + + Example: + + SecRule REQUEST_COOKIES:JSESSIONID "!^$" nolog,phase:1,pass,chain +SecAction setsid:%{REQUEST_COOKIES:JSESSIONID} +SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ + "phase:2,t:none,t:lowercase,t:normalisePath,log,allow,\ +setvar:session.suspicious=1,expirevar:session.suspicious=3600,phase:1" + + Note + + You should use expirevar actions at the same time that you use + setvar actions in order to keep the indented expiration time. If they + are used on their own (perhaps in a SecAction directive) the expire time + could get re-set. When variables are removed from collections, and there + are no other changes, collections are not written to disk at the end of + request. This is because the variables can always be expired again when + the collection is read again on a subsequent request. +
+ +
+ <literal>id</literal> + + Description: Assigns a unique ID to the rule + or chain. + + Action Group: Meta-data + + Example: + + SecRule &REQUEST_HEADERS:Host "@eq 0" \ + "log,id:60008,severity:2,msg:'Request Missing a Host Header'" + + Note + + These are the reserved ranges: + + + + 1-99,999; reserved for local (internal) use. Use as you see + fit but do not use this range for rules that are distributed to + others. + + + + 100,000-199,999; reserved for internal use of the engine, to + assign to rules that do not have explicit IDs. + + + + 200,000-299,999; reserved for rules published at + modsecurity.org. + + + + 300,000-399,999; reserved for rules published at + gotroot.com. + + + + 400,000-419,999; unused (available for reservation). + + + + 420,000-429,999; reserved for ScallyWhack. + + + + 430,000-699,999; unused (available for reservation). + + + + 700,000-799,999; reserved for Ivan Ristic. + + + + 900,000-999,999; reserved for the Core Rules + project. + + + + 1,000,000 and above; unused (available for + reservation). + + +
+ +
+ <literal>initcol</literal> + + Description: Initialises a named persistent + collection, either by loading data from storage or by creating a new + collection in memory. + + Action Group: Non-disruptive + + Example: The following example initiates IP address + tracking. + + SecAction phase:1,initcol:ip=%{REMOTE_ADDR},nolog + + Note + + Normally you will want to use phase:1 along + with initcol so that the collection is available in + all phases. + + Collections are loaded into memory when the initcol action is + encountered. The collection in storage will be persisted (and the + appropriate counters increased) only if it was + changed during transaction processing. + + See the "Persistant Storage" section for further details. +
+ +
+ <literal>log</literal> + + Description: Indicates that a successful + match of the rule needs to be logged. + + Action Group: Non-disruptive + + Example: + + SecAction phase:1,initcol:ip=%{REMOTE_ADDR},log + + Note + + This action will log matches to the Apache error log file and the + ModSecurity audit log. +
+ +
+ <literal>logdata</literal> + + Description: Allows a data fragment to be + logged as part of the alert message. + + Action Group: Non-disruptive + + Example: + + SecRule &ARGS:p "@eq 0" "log,logdata:'%{TX.0}'" + + Note + + The logdata information appears in the error and/or audit log + files and is not sent back to the client in response headers. Macro + expansion is preformed so you may use variable names such as %{TX.0}, + etc. The information is properly escaped for use with logging binary + data. +
+ +
+ <literal>msg</literal> + + Description: Assigns a custom message to the + rule or chain. + + Action Group: Meta-data + + Example: + + SecRule &REQUEST_HEADERS:Host "@eq 0" \ + "log,id:60008,severity:2,msg:'Request Missing a Host Header'" + + Note + + The msg information appears in the error and/or audit log files + and is not sent back to the client in response headers. +
+ +
+ <literal>multiMatch</literal> + + Description: If enabled ModSecurity will + perform multiple operator invocations for every target, before and after + every anti-evasion transformation is performed. + + Action Group: Non-disruptive + + Example: + + SecDefaultAction log,deny,phase:1,t:removeNulls,t:lowercase +SecRule ARGS "attack" multiMatch + + Note + + Normally, variables are evaluated once, only after all + transformation functions have completed. With multiMatch, variables are + checked against the operator before and after every transformation + function that changes the input. +
+ +
+ <literal>noauditlog</literal> + + Description: Indicates that a successful + match of the rule should not be used as criteria whether the transaction + should be logged to the audit log. + + Action Group: Non-disruptive + + Example: + + SecRule REQUEST_HEADERS:User-Agent "Test" allow,noauditlog + + Note + + If the SecAuditEngine is set to On, all of the transactions will + be logged. If it is set to RelevantOnly, then you can control it with + the noauditlog action. Even if the noauditlog action is applied to a + specific rule and a rule either before or after triggered an audit + event, then the transaction will be logged to the audit log. The correct + way to disable audit logging for the entire transaction is to use + "ctl:auditEngine=Off" +
+ +
+ <literal>nolog</literal> + + Description: Prevents rule matches from + appearing in both the error and audit logs. + + Action Group: Non-disruptive + + Example: + + SecRule REQUEST_HEADERS:User-Agent "Test" allow,nolog + + Note + + The nolog action also implies noauditlog. +
+ +
+ <literal>pass</literal> + + Description: Continues processing with the + next rule in spite of a successful match. + + Action Group: Disruptive + + Example1: + + SecRule REQUEST_HEADERS:User-Agent "Test" log,pass + + When using pass with SecRule with multiple + targets, all targets will be processed and + all non-disruptive actions will trigger for + every match found. In the second example the + TX:test target would be incremented by 1 for each matching + argument. + + Example2: + + SecRule ARGS "test" log,pass,setvar:TX.test=+1 + + Note + + The transaction will not be interrupted but a log will be + generated for each matching target (unless logging has been + suppressed). +
+ +
+ <literal>pause</literal> + + Description: Pauses transaction processing + for the specified number of milliseconds. + + Action Group: Non-disruptive + + Example: + + SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403,pause:5000 + + Note + + This feature can be of limited benefit for slowing down Brute + Force Scanners, however use with care. If you are under a Denial of + Service type of attack, the pause feature may make matters worse as this + feature will cause child processes to sit idle until the pause is + completed. +
+ +
+ <literal>phase</literal> + + Description: Places the rule (or the rule + chain) into one of five available processing phases. + + Action Group: Meta-data + + Example: + + SecDefaultAction log,deny,phase:1,t:removeNulls,t:lowercase +SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 + + Note + + Keep in mind that is you specify the incorrect phase, the target + variable that you specify may be empty. This could lead to a false + negative situation where your variable and operator (RegEx) may be + correct, but it misses malicious data because you specified the wrong + phase. +
+ +
+ prepend + + Description: Prepends text given as parameter + to the response body. For this action to work content injection must be + enabled by setting SecContentInjection to + On. Also make sure you check the content type of the + response before you make changes to it (e.g. you don't want to inject + stuff into images). + + Action Group: Non-disruptive + + Processing Phases: 3 and 4. + + Example: + + SecRule RESPONSE_CONTENT_TYPE ^text/html "phase:3,nolog,pass,prepend:'Header<br>'" + + + While macro expansion is allowed in the additional content, you + are strongly cautioned against inserting user defined data + fields. + +
+ +
+ <literal>proxy</literal> + + Description: Intercepts transaction by + forwarding request to another web server using the proxy backend. + + Action Group: Disruptive + + Example: + + SecRule REQUEST_HEADERS:User-Agent "Test" log,proxy:http://www.honeypothost.com/ + + Note + + For this action to work, mod_proxy must also be installed. This + action is useful if you would like to proxy matching requests onto a + honeypot webserver. +
+ +
+ <literal>redirect</literal> + + Description: Intercepts transaction by + issuing a redirect to the given location. + + Action Group: Disruptive + + Example: + + SecRule REQUEST_HEADERS:User-Agent "Test" \ + log,redirect:http://www.hostname.com/failed.html + + Note + + If the status action is present + and its value is acceptable (301, 302, 303, or 307) it will be used for + the redirection. Otherwise status code 302 will be used. +
+ +
+ <literal>rev</literal> + + Description: Specifies rule revision. + + Action Group: Meta-data + + Example: + + SecRule REQUEST_METHOD "^PUT$" "id:340002,rev:1,severity:2,msg:'Restricted HTTP function'" + + Note + + This action is used in combination with the id action to allow the same rule ID to be used + after changes take place but to still provide some indication the rule + changed. +
+ +
+ <literal>sanitiseArg</literal> + + Description: Sanitises (replaces each byte + with an asterisk) a named request argument prior to audit + logging. + + Action Group: Non-disruptive + + Example: + + SecAction nolog,phase:2,sanitiseArg:password + + Note + + The sanitize actions do not sanitize any data within the actual + raw requests but only on the copy of data within memory that is set to + log to the audit log. It will not sanitize the data in the + modsec_debug.log file (if the log level is set high enough to capture + this data). +
+ +
+ <literal>sanitiseMatched</literal> + + Description: Sanitises the variable (request + argument, request header, or response header) that caused a rule + match. + + Action Group: Non-disruptive + + Example: This action can be used to sanitise arbitrary transaction + elements when they match a condition. For example, the example below + will sanitise any argument that contains the word + password in the name. + + SecRule ARGS_NAMES password nolog,pass,sanitiseMatched + + Note + + Same note as sanitiseArg. +
+ +
+ <literal>sanitiseRequestHeader</literal> + + Description: Sanitises a named request + header. + + Action Group: Non-disruptive + + Example: This will sanitise the data in the Authorization + header. + + SecAction log,phase:1,sanitiseRequestHeader:Authorization + + Note + + Same note as sanitiseArg. +
+ +
+ <literal>sanitiseResponseHeader</literal> + + Description: Sanitises a named response + header. + + Action Group: Non-disruptive + + Example: This will sanitise the Set-Cookie data sent to the + client. + + SecAction log,phase:3,sanitiseResponseHeader:Set-Cookie + + Note + + Same note as sanitiseArg. +
+ +
+ <literal>severity</literal> + + Description: Assigns severity to the rule it + is placed with. + + Action Group: Meta-data + + Example: + + SecRule REQUEST_METHOD "^PUT$" "id:340002,rev:1,severity:CRITICAL,msg:'Restricted HTTP function'" + + Note + + Severity values in ModSecurity follow those of syslog, as + below: + + + + 0 - EMERGENCY + + + + 1 - ALERT + + + + 2 - CRITICAL + + + + 3 - ERROR + + + + 4 - WARNING + + + + 5 - NOTICE + + + + 6 - INFO + + + + 7 - DEBUG + + + + It is possible to specify severity levels using either the + numerical values or the text values. You should always specify severity + levels using the text values. The use of the numerical values is + deprecated (as of v2.5.0) and may be removed in one of the susequent + major updates. +
+ +
+ <literal>setuid</literal> + + Description: Special-purpose action that + initialises the USER + collection. + + Action Group: Non-disruptive + + Example: + + SecAction setuid:%{REMOTE_USER},nolog + + Note + + After initialisation takes place the variable USERID will be available for use in the + subsequent rules. +
+ +
+ <literal>setsid</literal> + + Description: Special-purpose action that + initialises the SESSION + collection. + + Action Group: Non-disruptive + + Example: + + # Initialise session variables using the session cookie value +SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass +SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} + + Note + + On first invocation of this action the collection will be empty + (not taking the predefined variables into account - see initcol for more information). On subsequent + invocations the contents of the collection (session, in this case) will + be retrieved from storage. After initialisation takes place the + variable SESSIONID will be available + for use in the subsequent rules.This action understands each application + maintains its own set of sessions. It will utilise the current web + application ID to create a session namespace. +
+ +
+ <literal>setenv</literal> + + Description: Creates, removes, or updates an + environment variable. + + Action Group: Non-disruptive + + Examples: + + To create a new variable (if you omit the value 1 will be used): + + setenv:name=value + + To remove a variable: + + setenv:!name + + Note + + This action can be used to establish communication with other + Apache modules. +
+ +
+ <literal>setvar</literal> + + Description: Creates, removes, or updates a + variable in the specified collection. + + Action Group: Non-disruptive + + Examples: + + To create a new variable: + + setvar:tx.score=10 + + To remove a variable prefix the name with exclamation mark: + + setvar:!tx.score + + To increase or decrease variable value use + and - + characters in front of a numerical value: + + setvar:tx.score=+5 +
+ +
+ <literal>skip</literal> + + Description: Skips one or more rules (or + chains) on successful match. + + Action Group: Flow + + Example: + + SecRule REQUEST_URI "^/$" \ +"phase:2,chain,t:none,skip:2" +SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" +SecRule REQUEST_HEADERS:User-Agent "^Apache \(internal dummy connection\)$" "t:none" +SecRule &REQUEST_HEADERS:Host "@eq 0" \ + "deny,log,status:400,id:960008,severity:4,msg:'Request Missing a Host Header'" +SecRule &REQUEST_HEADERS:Accept "@eq 0" \ + "log,deny,log,status:400,id:960015,msg:'Request Missing an Accept Header'" + + Note + + Skip only applies to the current processing phase and not + necessarily the order in which the rules appear in the configuration + file. If you group rules by processing phases, then skip should work as + expected. This action can not be used to skip rules within one chain. + Accepts a single parameter denoting the number of rules (or chains) to + skip. +
+ +
+ <literal>skipAfter</literal> + + Description: Skips rules (or chains) on + successful match resuming rule execution after the specified rule ID or + marker (see SecMarker) is found. + + Action Group: Flow + + Example: + + SecRule REQUEST_URI "^/$" "chain,t:none,skipAfter:960015" +SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" +SecRule REQUEST_HEADERS:User-Agent "^Apache \(internal dummy connection\)$" "t:none" +SecRule &REQUEST_HEADERS:Host "@eq 0" \ + "deny,log,status:400,id:960008,severity:4,msg:'Request Missing a Host Header'" +SecRule &REQUEST_HEADERS:Accept "@eq 0" \ + "log,deny,log,status:400,id:960015,msg:'Request Missing an Accept Header'" + + Note + + SkipAfter only applies to the current + processing phase and not necessarily the order in which the rules appear + in the configuration file. If you group rules by processing phases, then + skip should work as expected. This action can not be used to skip rules + within one chain. Accepts a single parameter denoting the last rule ID + to skip. +
+ +
+ <literal>status</literal> + + Description: Specifies the response status + code to use with actions deny + and redirect. + + Action Group: Data + + Example: + + SecDefaultAction log,deny,status:403,phase:1 + + Note + + Status actions defined in Apache scope locations (such as + Directory, Location, etc...) may be superseded by phase:1 action + settings. The Apache ErrorDocument directive will be triggered if + present in the configuration. Therefore if you have previously defined a + custom error page for a given status then it will be executed and its + output presented to the user. +
+ +
+ <literal>t</literal> + + Description: This action can be used which + transformation function should be used against the specified variables + before they (or the results, rather) are run against the operator + specified in the rule. + + Action Group: Non-disruptive + + Example: + + SecDefaultAction log,deny,phase:1,t:removeNulls,t:lowercase +SecRule REQUEST_COOKIES:SESSIONID "47414e81cbbef3cf8366e84eeacba091" \ + log,deny,status:403,t:md5,t:hexEncode + + Note + + Any transformation functions that you specify in a SecRule will be + in addition to previous ones specified in SecDefaultAction. Use of + "t:none" will remove all transformation functions for the specified + rule. +
+ +
+ <literal>tag</literal> + + Description: Assigns custom text to a rule or + chain. + + Action Group: Meta-data + + Example: + + SecRule REQUEST_FILENAME "\b(?:n(?:map|et|c)|w(?:guest|sh)|cmd(?:32)?|telnet|rcmd|ftp)\.exe\b" \ + "t:none,t:lowercase,deny,msg:'System Command Access',id:'950002',\ +tag:'WEB_ATTACK/FILE_INJECTION',tag:'OWASP/A2',severity:'2'" + + Note + + The tag information appears in the error and/or audit log files. + Its intent is to be used to automate classification of rules and the + alerts generated by rules. Multiple tags can be used per + rule/chain. +
+ +
+ <literal>xmlns</literal> + + Description: This action should be used + together with an XPath expression to register a namespace. + + Action Group: Data + + Example: + + SecRule REQUEST_HEADERS:Content-Type "text/xml" \ + "phase:1,pass,ctl:requestBodyProcessor=XML,ctl:requestBodyAccess=On, \ + xmlns:xsd="http://www.w3.org/2001/XMLSchema" +SecRule XML:/soap:Envelope/soap:Body/q1:getInput/id() "123" phase:2,deny +
+
+ +
+ Operators + + A number of operators can be used in rules, as documented below. The + operator syntax uses the @ symbol followed by the + specific operator name. + +
+ <literal>beginsWith</literal> + + Description: This operator is a string + comparison and returns true if the parameter value is found at the + beginning of the input. Macro expansion is performed so you may use + variable names such as %{TX.1}, etc. + + Example: + + SecRule REQUEST_LINE "!@beginsWith GET" t:none,deny,status:403 +SecRule REQUEST_ADDR "^(.*)\.\d+$" deny,status:403,capture,chain +SecRule ARGS:gw "!@beginsWith %{TX.1}" +
+ +
+ <literal>contains</literal> + + Description: This operator is a string + comparison and returns true if the parameter value is found anywhere in + the input. Macro expansion is performed so you may use variable names + such as %{TX.1}, etc. + + Example: + + SecRule REQUEST_LINE "!@contains .php" t:none,deny,status:403 +SecRule REQUEST_ADDR "^(.*)$" deny,status:403,capture,chain +SecRule ARGS:ip "!@contains %{TX.1}" +
+ +
+ <literal>endsWith</literal> + + Description: This operator is a string + comparison and returns true if the parameter value is found at the end + of the input. Macro expansion is performed so you may use variable names + such as %{TX.1}, etc. + + Example: + + SecRule REQUEST_LINE "!@endsWith HTTP/1.1" t:none,deny,status:403 +SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}" t:none,deny,status:403 +
+ +
+ <literal>eq</literal> + + Description: This operator is a numerical + comparison and stands for "equal to." + + Example: + + SecRule &REQUEST_HEADERS_NAMES "@eq 15" + + Macro expansion is performed so you may use variable names such as + %{TX.1}, etc. +
+ +
+ <literal>ge</literal> + + Description: This operator is a numerical + comparison and stands for "greater than or equal to." + + Example: + + SecRule &REQUEST_HEADERS_NAMES "@ge 15" + + Macro expansion is performed so you may use variable names such as + %{TX.1}, etc. +
+ +
+ <literal>geoLookup</literal> + + Description: This operator looks up various + data fields from an IP address or hostname in the target data. The + results will be captured in the GEO + collection. + + You must provide a database via SecGeoLookupDb before this operator can be + used. + + + This operator matches and the action is executed on a + successful lookup. For this reason, you probably want to + use the pass,nolog actions. This allows for + setvar and other non-disruptive + actions to be executed on a match. If you wish to block on a failed + lookup, then do something like this (look for an empty GEO + collection): + + SecGeoLookupDb /usr/local/geo/data/GeoLiteCity.dat +... +SecRule REMOTE_ADDR "@geoLookup" "pass,nolog" +SecRule &GEO "@eq 0" "deny,status:403,msg:'Failed to lookup IP'" + + + See the GEO variable for an + example and more information on various fields available. +
+ +
+ <literal>gt</literal> + + Description: This operator is a numerical + comparison and stands for "greater than." + + Example: + + SecRule &REQUEST_HEADERS_NAMES "@gt 15" + + Macro expansion is performed so you may use variable names such as + %{TX.1}, etc. +
+ +
+ <literal>inspectFile</literal> + + Description: Executes the external + script/binary given as parameter to the operator against every file + extracted from the request. As of v2.5.0, if the supplied filename is + not absolute it is treated as relative to the directory in which the + configuration file resides. Also as of v2.5.0, if the filename is + determined to be a Lua script (based on its extension) the script will + be processed by the internal engine. As such it will have full access to + the ModSecurity context. + + Example of using an external binary/script: + + # Execute external script to validate uploaded files. +SecRule FILES_TMPNAMES "@inspectFile /opt/apache/bin/inspect_script.pl" + + Example of using Lua script: + + SecRule FILES_TMPNANMES "@inspectFile inspect.lua" + + Script inspect.lua: + + function main(filename) + -- Do something to the file to verify it. In this example, we + -- read up to 10 characters from the beginning of the file. + local f = io.open(filename, "rb"); + local d = f:read(10); + f:close(); + + -- Return null if there is no reason to believe there is ansything + -- wrong with the file (no match). Returning any text will be taken + -- to mean a match should be trigerred. + return null; +end +
+ +
+ <literal>le</literal> + + Description: This operator is a numerical + comparison and stands for "less than or equal to." + + Example: + + SecRule &REQUEST_HEADERS_NAMES "@le 15" + + Macro expansion is performed so you may use variable names such as + %{TX.1}, etc. +
+ +
+ <literal>lt</literal> + + Description: This operator is a numerical + comparison and stands for "less than." + + Example: + + SecRule &REQUEST_HEADERS_NAMES "@lt 15" + + Macro expansion is performed so you may use variable names such as + %{TX.1}, etc. +
+ +
+ <literal>pm</literal> + + Description: Phrase Match operator. This + operator uses a set based matching engine (Aho-Corasick) for faster + matches of keyword lists. It will match any one of its arguments + anywhere in the target value. The match is case insensitive. + + Example: + + SecRule REQUEST_HEADERS:User-Agent "@pm WebZIP WebCopier Webster WebStripper SiteSnagger ProWebWalker CheeseBot" "deny,status:403 + + The above would deny access with 403 if any of the words matched + within the User-Agent HTTP header value. +
+ +
+ <literal>pmFromFile</literal> + + Description: Phrase Match operator. This + operator uses a set based matching engine (Aho-Corasick) for faster + matches of keyword lists. This operator is the same as + @pm except that it takes a list of files as + arguments. It will match any one of the phrases listed in the file(s) + anywhere in the target value. + + Notes: + + + + The contents of the files should be one phrase per line. End + of line markers will be stripped from the phrases (LF and CRLF), and + whitespace is trimmed from both sides of the phrases. Empty lines + and comment lines (beginning with a '#') are ignored. + + + + To allow easier inclusion of phrase files with rulesets, + relative paths may be used to the phrase files. In this case, the + path of the file containing the rule is prepended to the phrase file + path. + + + + To allow easier matching of whole IP addresses, you can add + boundary characters to the phrases. For example, use "/1.2.3.4/" + instead of "1.2.3.4". You can then insert these characters into the + target prior to a match: + + SecAction "phase:1,pass,nolog,setvar:tx.remote_addr=/%{REMOTE_ADDR}/" +SecRule TX:REMOTE_ADDR "@pmFromFile ip-blacklist.txt" "deny,status:403 + +# ip-blacklist.txt contents: +# NOTE: All IPs must be prefixed/suffixed with "/" as the rules +# will add in this character as a boundary to ensure +# the entire IP is matched. +# SecAction "phase:1,pass,nolog,setvar:tx.remote_addr='/%{REMOTE_ADDR}/'" +/1.2.3.4/ +/5.6.7.8/ + + + + Example: + + SecRule REQUEST_HEADERS:User-Agent "@pm /path/to/blacklist1 blacklist2" "deny,status:403 + + The above would deny access with 403 if any of the patterns in the + two files matched within the User-Agent HTTP header value. The + blacklist2 file would need to be placed in the same + path as the file containing the rule. +
+ +
+ <literal>rbl</literal> + + Description: Look up the parameter in the RBL + given as parameter. Parameter can be an IPv4 address, or a + hostname. + + Example: + + SecRule REMOTE_ADDR "@rbl sc.surbl.org" +
+ +
+ <literal>rx</literal> + + Description: Regular expression operator. + This is the default operator, so if the "@" operator is not defined, it + is assumed to be rx. + + Example: + + SecRule REQUEST_HEADERS:User-Agent "@rx nikto" + + Note + + Regular expressions are handled by the PCRE library (http://www.pcre.org). ModSecurity + compiles its regular expressions with the following settings: + + + + The entire input is treated as a single line, even when there + are newline characters present. + + + + All matches are case-sensitive. If you do not care about case + sensitivity you either need to implement the lowercase transformation function, or use + the per-pattern(?i)modifier, as + allowed by PCRE. + + + + The PCRE_DOTALL and + PCRE_DOLLAR_ENDONLY flags are set + during compilation, meaning a single dot will match any character, + including the newlines and a $ + end anchor will not match a trailing newline character. + + +
+ +
+ <literal>streq</literal> + + Description: This operator is a string + comparison and returns true if the parameter value matches the input + exactly. Macro expansion is performed so you may use variable names such + as %{TX.1}, etc. + + Example: + + SecRule ARGS:foo "!@streq bar" t:none,deny,status:403 +SecRule REQUEST_ADDR "^(.*)$" deny,status:403,capture,chain +SecRule REQUEST_HEADERS:Ip-Address "!@streq %{TX.1}" +
+ +
+ <literal>validateByteRange</literal> + + Description: Validates the byte range used in + the variable falls into the specified range. + + Example: + + SecRule ARGS:text "@validateByteRange 10, 13, 32-126" + + Note + + You can force requests to consist only of bytes from a certain + byte range. This can be useful to avoid stack overflow attacks (since + they usually contain "random" binary content). Default range values are + 0 and 255, i.e. all byte values are allowed. This directive does not + check byte range in a POST payload when + multipart/form-data encoding (file upload) is used. + Doing so would prevent binary files from being uploaded. However, after + the parameters are extracted from such request they are checked for a + valid range. + + validateByteRange is similar to the ModSecurity 1.X + SecFilterForceByteRange Directive however since it works in a rule + context, it has the following differences: + + + + You can specify a different range for different + variables. + + + + It has an "event" context (id, msg....) + + + + It is executed in the flow of rules rather than being a built + in pre-check. + + +
+ +
+ <literal>validateDTD</literal> + + Description: Validates the DOM tree generated + by the XML request body processor against the supplied DTD. + + Example: + + SecDefaultAction log,deny,status:403,phase:2 +SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ + phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML +SecRule REQBODY_PROCESSOR "!^XML$" nolog,pass,skipAfter:12345 +SecRule XML "@validateDTD /path/to/apache2/conf/xml.dtd" "deny,id:12345" + + + This operator requires request body to be processed as + XML. + +
+ +
+ <literal>validateSchema</literal> + + Description: Validates the DOM tree generated + by the XML request body processor against the supplied XML + Schema. + + Example: + + SecDefaultAction log,deny,status:403,phase:2 +SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ + phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML +SecRule REQBODY_PROCESSOR "!^XML$" nolog,pass,skipAfter:12345 +SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd" "deny,id:12345" + + + This operator requires request body to be processed as + XML. + +
+ +
+ <literal>validateUrlEncoding</literal> + + Description: Verifies the encodings used in + the variable (if any) are valid. + + Example: + + SecRule ARGS "@validateUrlEncoding" + + Note + + URL encoding is an HTTP standard for encoding byte values within a + URL. The byte is escaped with a % followed by two hexadecimal values + (0-F). This directive does not check encoding in a POST payload when the + multipart/form-data encoding (file upload) is used. + It is not necessary to do so because URL encoding is not used for this + encoding. +
+ +
+ <literal>validateUtf8Encoding</literal> + + Description: Verifies the variable is a valid + UTF-8 encoded string. + + Example: + + SecRule ARGS "@validateUtf8Encoding" + + Note + + UTF-8 encoding is valid on most web servers. Integer values + between 0-65535 are encoded in a UTF-8 byte sequence that is escaped by + percents. The short form is two bytes in length. + + check for three types of errors: + + + + Not enough bytes. UTF-8 supports two, three, four, five, and + six byte encodings. ModSecurity will locate cases when a byte or + more is missing. + + + + Invalid encoding. The two most significant bits in most + characters are supposed to be fixed to 0x80. Attackers can use this + to subvert Unicode decoders. + + + + Overlong characters. ASCII characters are mapped directly into + the Unicode space and are thus represented with a single byte. + However, most ASCII characters can also be encoded with two, three, + four, five, and six characters thus tricking the decoder into + thinking that the character is something else (and, presumably, + avoiding the security check). + + +
+ +
+ <literal>verifyCC</literal> + + Description: This operator verifies a given + regular expression as a potential credit card number. It first matches + with a single generic regular expression then runs the resulting match + through a Luhn checksum algorithm to further verify it as a potential + credit card number. + + Example: + + SecRule ARGS "@verifyCC \d{13,16}" \ + "phase:2,sanitiseMatched,log,auditlog,pass,msg:'Potential credit card number'" +
+ +
+ <literal>within</literal> + + Description: This operator is a string + comparison and returns true if the input value is found anywhere within + the parameter value. Note that this is similar to + @contains, except that the target and match values + are reversed. Macro expansion is performed so you may use variable names + such as %{TX.1}, etc. + + Example: + + SecRule REQUEST_METHOD "!@within get,post,head" t:lowercase,deny,status:403 + +SecAction "pass,setvar:'tx.allowed_methods=get,post,head'" +SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:lowercase,deny,status:403 +
+
+ +
+ Macro Expansion + + Macros allow for using place holders in rules that will be expanded + out to their values at runtime. Currently only variable expansion is + supported, however more options may be added in future versions of + ModSecurity. + + Format: + + %{VARIABLE} +%{COLLECTION.VARIABLE} + + Macro expansion can be used in actions such as initcol, setsid, + setuid, setvar, setenv, logdata. Operators that are evaluated at runtime + support expansion and are noted above. Such operators include @beginsWith, + @endsWith, @contains, @within and @streq. You cannot use macro expansion + for operators that are "compiled" such as @pm, @rx, etc. as these + operators have their values fixed at configure time for efficiency. + + Some values you may want to expand include: TX, REMOTE_ADDR, USERID, + HIGHEST_SEVERITY, MATCHED_VAR, MATCHED_VAR_NAME, MULTIPART_STRICT_ERROR, + RULE, SESSION, USERID, among others. +
+ +
+ Persistant Storage + + At this time it is only possible to have three collections in which + data is stored persistantly (i.e. data available to multiple requests). + These are: IP, SESSION and USER. + + Every collection contains several built-in variables that are + available and are read-only unless otherwise specified: + + + + CREATE_TIME - date/time of + the creation of the collection. + + + + IS_NEW - set to 1 if the + collection is new (not yet persisted) otherwise set to 0. + + + + KEY - the value of the + initcol variable (the client's IP address in the example). + + + + LAST_UPDATE_TIME - date/time + of the last update to the collection. + + + + TIMEOUT - date/time in + seconds when the collection will be updated on disk from memory (if no + other updates occur). This variable may be set if you wish to specifiy + an explicit expiration time (default is 3600 seconds). + + + + UPDATE_COUNTER - how many + times the collection has been updated since creation. + + + + UPDATE_RATE - is the average + rate updates per minute since creation. + + + + To create a collection to hold session variables (SESSION) use action setsid. To create a collection to hold user + variables (USER) use action setuid. To create a collection to hold client + address variables (IP) use action + initcol. + + + ModSecurity implements atomic updates of persistent variables only + for integer variables (counters) at this time. Variables are read from + storage whenever initcol is encountered in the rules + and persisted at the end of request processing. Counters are adjusted by + applying a delta generated by re-reading the persisted data just before + being persisted. This keeps counter data consistent even if the counter + was modified and persisted by another thread/process during the + transaction. + + + + ModSecurity uses a Berkley Database (SDBM) for persistant storage. + This type of database is generally limited to storing a maximum of 1008 + bytes per key. This may be a limitation if you are attempting to store a + considerable amount of data in variables for a single key. Some of this + limitation is planned to be reduced in a future version of + ModSecurity. + +
+ +
+ Miscellaneous Topics + + + +
+ Impedance Mismatch + + Web application firewalls have a difficult job trying to make + sense of data that passes by, without any knowledge of the application + and its business logic. The protection they provide comes from having an + independent layer of security on the outside. Because data validation is + done twice, security can be increased without having to touch the + application. In some cases, however, the fact that everything is done + twice brings problems. Problems can arise in the areas where the + communication protocols are not well specified, or where either the + device or the application do things that are not in the specification. + In such cases it may be possible to design payload that will be + interpreted in one way by one device and in another by the other device. + This problem is better known as Impedance Mismatch. It can be exploited + to evade the security devices. + + While we will continue to enhance ModSecurity to deal with various + evasion techniques the problem can only be minimized, but never solved. + With so many different application backend chances are some will always + do something completely unexpected. The only solution is to be aware of + the technologies in the backend when writing rules, adapting the rules + to remove the mismatch. See the next section for some examples. + +
+ PHP Peculiarities for ModSecurity Users + + When writing rules to protect PHP applications you need to pay + attention to the following facts: + + + + When "register_globals" is set to "On" request parameters + are automatically converted to script variables. In some PHP + versions it is even possible to override the $GLOBALS + array. + + + + Whitespace at the beginning of parameter names is ignored. + (This is very dangerous if you are writing rules to target + specific named variables.) + + + + The remaining whitespace (in parameter names) is converted + to underscores. The same applies to dots and to a "[" if the + variable name does not contain a matching closing bracket. + (Meaning that if you want to exploit a script through a variable + that contains an underscore in the name you can send a parameter + with a whitespace or a dot instead.) + + + + Cookies can be treated as request parameters. + + + + The discussion about variable names applies equally to the + cookie names. + + + + The order in which parameters are taken from the request and + the environment is EGPCS (environment, GET, POST, Cookies, + built-in variables). This means that a POST parameter will + overwrite the parameters transported on the request line (in + QUERY_STRING). + + + + When "magic_quotes_gpc" is set to "On" PHP will use + backslash to escape the following characters: single quote, double + quote, backslash, and the nul byte. + + + + If "magic_quotes_sybase" is set to "On" only the single + quote will be escaped using another single quote. In this case the + "magic_quotes_gpc" setting becomes irrelevant. The + "magic_quotes_sybase" setting completely overrides the + "magic_quotes_gpc" behaviour but "magic_quotes_gpc" still must be + set to "On" for the Sybase-specific quoting to be work. + + + + PHP will also automatically create nested arrays for you. + For example "p[x][y]=1" results in a total of three + variables. + + +
+
+
+
diff --git a/2.5.13/2.5.x/doc/modsecurity2-data-formats.xml b/2.5.13/2.5.x/doc/modsecurity2-data-formats.xml new file mode 100644 index 00000000..fdb76541 --- /dev/null +++ b/2.5.13/2.5.x/doc/modsecurity2-data-formats.xml @@ -0,0 +1,979 @@ + + +
+ ModSecurity 2 Data Formats + + Version 2.5.10-dev1 (March 24, 2009) + + 2004-2010 + Trustwave Holdings, Inc. (http://www.trustwave.com) + + + The purpose of this document is to describe the formats of the ModSecurity alert messages, + transaction logs and communication protocols, which would not only allow for a better + understanding what ModSecurity does but also for an easy integration with third-party tools + and products. +
+ Alerts + As part of its operations ModSecurity will emit alerts, which are either + warnings (non-fatal) or errors (fatal, + usually leading to the interception of the transaction in question). Below is an example + of a ModSecurity alert entry: + Access denied with code 505 (phase 1). Match of "rx + ^HTTP/(0\\\\.9|1\\\\.[01])$" against "REQUEST_PROTOCOL" required. + [id "960034"] [msg "HTTP protocol version is not allowed by policy"] + [severity "CRITICAL"] [uri "/"] [unique_id "PQaTTVBEUOkAAFwKXrYAAAAM"] + + Alerts will only ever contain one line of text but we've broken the above example + into multiple lines to make it fit into the page. + + Each alert entry begins with the engine message, which describes what ModSecurity did + and why. For + example:Access denied with code 505 (phase 1). Match of "rx + ^HTTP/(0\\\\.9|1\\\\.[01])$" against "REQUEST_PROTOCOL" required. +
+ Alert Action Description + The first part of the engine message tells you whether ModSecurity acted to + interrupt transaction or rule processing: + + + If the alert is only a warning, the first sentence will simply say + Warning. + + + If the transaction was intercepted, the first sentence will begin with + Access denied. What follows is the list of possible + messages related to transaction interception: + + + Access denied with code %0 - a response with + status code %0 was sent. + + + Access denied with connection close - + connection was abruptly closed. + + + Access denied with redirection to %0 using status + %1 - a redirection to URI %0 was + issued using status %1. + + + + + There is also a special message that ModSecurity emits where an allow action is executed. There are three variations of this + type of message: + + + Access allowed - rule engine stopped + processing rules (transaction was unaffected). + + + Access to phase allowed - rule engine stopped + processing rules in the current phase only. Subsequent phases will + be processed normally. Transaction was not affected by this rule but + it may be affected by any of the rules in the subsequent + phase. + + + Access to request allowed - rule engine + stopped processing rules in the current phase. Phases prior to + request execution in the backend (currently phases 1 and 2) will not + be processed. The response phases (currently phases 3 and 4) and + others (currently phase 5) will be processed as normal. Transaction + was not affected by this rule but it may be affected by any of the + rules in the subsequent phase. + + + + +
+
+ Alert Justification Description + The second part of the engine message explains why the alert + was generated. Since it is automatically generated from the rules it will be very + technical in nature, talking about operators and their parameters and give you + insight into what the rule looked like. But this message cannot give you insight + into the reasoning behind the rule. A well-written rule will always specify a + human-readable message (using the msg action) to provide further + information. + The format of the second part of the engine message depends on whether it was + generated by the operator (which happens on a match) or by the rule processor (which + happens where there is not a match, but the negation was used): + + + @beginsWith - String match %0 at + %1. + + + @contains - String match %0 at + %1. + + + @containsWord - String match %0 at + %1. + + + @endsWith - String match %0 at + %1. + + + @eq - Operator EQ matched %0 at + %1. + + + @ge - Operator GE matched %0 at + %1. + + + @geoLookup - Geo lookup for %0 succeeded at + %1. + + + @inspectFile - File %0 rejected by the + approver script %1: %2 + + + @le - Operator LE matched %0 at + %1. + + + @lt - Operator LT matched %0 at + %1. + + + @rbl - RBL lookup of %0 succeeded at + %1. + + + @rx - Pattern match %0 at + %1. + + + @streq - String match %0 at + %1. + + + @validateByteRange - Found %0 byte(s) in %1 + outside range: %2. + + + @validateDTD - XML: DTD validation + failed. + + + @validateSchema - XML: Schema validation + failed. + + + @validateUrlEncoding + + + Invalid URL Encoding: Non-hexadecimal digits used at + %0. + + + Invalid URL Encoding: Not enough characters at the end + of input at %0. + + + + + @validateUtf8Encoding + + + Invalid UTF-8 encoding: not enough bytes in character at + %0. + + + Invalid UTF-8 encoding: invalid byte value in character + at %0. + + + Invalid UTF-8 encoding: overlong character detected at + %0. + + + Invalid UTF-8 encoding: use of restricted character at + %0. + + + Invalid UTF-8 encoding: decoding error at + %0. + + + + + @verifyCC - CC# match %0 at + %1. + + + Messages not related to operators: + + + When SecAction directive is processed - + Unconditional match in SecAction. + + + When SecRule does not match but negation is used - + Match of %0 against %1 required. + + + + The parameters to the operators @rx and @pm (regular expression and text pattern, respectively) will be + truncated to 252 bytes if they are longer than this limit. In this case the + parameter in the alert message will be terminated with three dots. + +
+
+ Meta-data + The metadata fields are always placed at the end of the alert entry. Each metadata + field is a text fragment that consists of an open bracket followed by the metadata + field name, followed by the value and the closing bracket. What follows is the text + fragment that makes up the id metadata field. + [id "960034"] + The following metadata fields are currently used: + + + offset - The byte offset where a match occured within + the target data. This is not always available. + + + id - Unique rule ID, as specified by the id action. + + + rev - Rule revision, as specified by the rev action. + + + msg - Human-readable message, as specified by the + msg action. + + + severity - Event severity as text, as specified by the + severity action. The possible values (with their + corresponding numberical values in brackets) are EMERGENCY (0), ALERT (1), CRITICAL (2), ERROR (3), WARNING (4), NOTICE (5), INFO (6) and DEBUG (7). + + + unique_id - Unique event ID, generated + automatically. + + + uri - Request URI. + + + logdata - contains transaction data fragment, as + specified by the logdata action. + + +
+
+ Escaping + ModSecurity alerts will always contain text fragments that were taken from + configuration or the transaction. Such text fragments escaped before they are user + in messages, in order to sanitise the potentially dangerous characters. They are + also sometimes surrounded using double quotes. The escaping algorithm is as + follows: + + Characters 0x08 (BACKSPACE), + 0x0a (NEWLINE), 0x10 (CARRIAGE RETURN), 0x09 (HORIZONTAL TAB) and 0x0b (VERTICAL TAB) will be + represented as \b, \n, \r, \t and \v, + respectively. + + + Bytes from the ranges 0-0x1f and 0x7f-0xff (inclusive) will be represented as \xHH, where HH is the hexadecimal + value of the byte. + + + Backslash characters (\) will be represented as + \\. + + + Each double quote character will be represented as \", but only if the entire fragment is surrounded with + double quotes. + + +
+
+ Alerts in the Apache Error Log + Every ModSecurity alert conforms to the following format when it appears in the + Apache error log: + [Sun Jun 24 10:19:58 2007] [error] [client 192.168.0.1] + ModSecurity: ALERT_MESSAGE + The above is a standard Apache error log format. The ModSecurity: + prefix is specific to ModSecurity. It is used to allow quick + identification of ModSecurity alert messages when they appear in the same file next + to other Apache messages. + The actual message (ALERT_MESSAGE in the example above) is in + the same format as described in the Alerts section. + + Apache further escapes ModSecurity alert messages before writing them to the + error log. This means that all backslash characters will be doubled in the error + log. In practice, since ModSecurity will already represent a single backslash + within an untrusted text fragment as two backslashes, the end result in the + Apache error log will be four backslashes. Thus, if you + need to interpret a ModSecurity message from the error log, you should decode + the message part after the ModSecurity: prefix first. This + step will peel the first encoding layer. + +
+
+ Alerts in Audit Logs + Alerts are transported in the H section of the ModSecurity + Audit Log. Alerts will appear each on a separate line and in the order they were + generated by ModSecurity. Each line will be in the following format: + Message: ALERT_MESSAGE + Below is an example of an H section that contains two alert + messages: + --c7036611-H-- +Message: Warning. Match of "rx ^apache.*perl" against + "REQUEST_HEADERS:User-Agent" required. [id "990011"] [msg "Request + Indicates an automated program explored the site"] [severity "NOTICE"] +Message: Warning. Pattern match "(?:\\b(?:(?:s(?:elect\\b(?:.{1,100}?\\b + (?:(?:length|count|top)\\b.{1,100}?\\bfrom|from\\b.{1,100}?\\bwhere) + |.*?\\b(?:d(?:ump\\b.*\\bfrom|ata_type)|(?:to_(?:numbe|cha)|inst)r))|p_ + (?:(?:addextendedpro|sqlexe)c|(?:oacreat|prepar)e|execute(?:sql)?| + makewebt ..." at ARGS:c. [id "950001"] [msg "SQL Injection Attack. + Matched signature: union select"] [severity "CRITICAL"] +Stopwatch: 1199881676978327 2514 (396 2224 -) +Producer: ModSecurity v2.x.x (Apache 2.x) +Server: Apache/2.x.x + +--c7036611-Z-- +
+
+
+ Audit Log + ModSecurity records one transaction in a single audit log file. Below is an + example: + --c7036611-A-- +[09/Jan/2008:12:27:56 +0000] OSD4l1BEUOkAAHZ8Y3QAAAAH 209.90.77.54 64995 + 80.68.80.233 80 +--c7036611-B-- +GET //EvilBoard_0.1a/index.php?c='/**/union/**/select/**/1,concat(username, + char(77),password,char(77),email_address,char(77),info,char(77),user_level, + char(77))/**/from/**/eb_members/**/where/**/userid=1/*http://kamloopstutor. + com/images/banners/on.txt? HTTP/1.1 +TE: deflate,gzip;q=0.3 +Connection: TE, cslose +Host: www.example.com +User-Agent: libwww-perl/5.808 + +--c7036611-F-- +HTTP/1.1 404 Not Found +Content-Length: 223 +Connection: close +Content-Type: text/html; charset=iso-8859-1 + +--c7036611-H-- +Message: Warning. Match of "rx ^apache.*perl" against + "REQUEST_HEADERS:User-Agent" required. [id "990011"] [msg "Request + Indicates an automated program explored the site"] [severity "NOTICE"] +Message: Warning. Pattern match "(?:\\b(?:(?:s(?:elect\\b(?:.{1,100}?\\b + (?:(?:length|count|top)\\b.{1,100}?\\bfrom|from\\b.{1,100}?\\bwhere) + |.*?\\b(?:d(?:ump\\b.*\\bfrom|ata_type)|(?:to_(?:numbe|cha)|inst)r))|p_ + (?:(?:addextendedpro|sqlexe)c|(?:oacreat|prepar)e|execute(?:sql)?| + makewebt ..." at ARGS:c. [id "950001"] [msg "SQL Injection Attack. + Matched signature: union select"] [severity "CRITICAL"] +Stopwatch: 1199881676978327 2514 (396 2224 -) +Producer: ModSecurity v2.x.x (Apache 2.x) +Server: Apache/2.x.x + +--c7036611-Z-- + + The file consist of multiple sections, each in different format. Separators are used + to define sections: + --c7036611-A-- + A separator always begins on a new line and conforms to the following format: + + + Two dashes + + + Unique boundary, which consists from several hexadecimal characters. + + + One dash character. + + + Section identifier, currently a single uppercase letter. + + + Two trailing dashes. + + + Refer to the documentation for SecAuditLogParts for the explanation + of each part. +
+ Parts + This section documents the audit log parts available in ModSecurity 2.x. They are: + + A - audit log header + + + B - request headers + + + C - request body + + + D - intended response headers (NOT + IMPLEMENTED) + + + E - intended response body + + + F - response headers + + + G - response body (NOT + IMPLEMENTED) + + + H - audit log trailer + + + I - reduced multipart request + body + + + J - multipart files information + (NOT IMPLEMENTED) + + + K - matched rules + information + + + Z - audit log footer + + +
+ Audit Log Header (<literal>A</literal>) + ModSecurity 2.x audit log entries always begin with the header part. For + example: + --c7036611-A-- +[09/Jan/2008:12:27:56 +0000] OSD4l1BEUOkAAHZ8Y3QAAAAH 209.90.77.54 64995 + 80.68.80.233 80 + The header contains only one line, with the following information on + it: + + + Timestamp + + + Unique transaction ID + + + Source IP address (IPv4 or IPv6) + + + Source port + + + Destination IP address (IPv4 or IPv6) + + + Destination port + + +
+
+ Request Headers (<literal>B</literal>) + The request headers part contains the request line and the request headers. + The information present in this part will not be identical to that sent by the + client responsible for the transaction. ModSecurity 2.x for Apache does not have + access to the raw data; it sees what Apache itself sees. While the end result + may be identical to the raw request, differences are possible in some + areas: + + + If any of the fields are NUL-terminated, Apache + will only see the content prior to the NUL. + + + Headers that span multiple lines (feature known as header folding) + will be collapsed into a single line. + + + Multiple headers with the same name will be combined into a single + header (as allowed by the HTTP RFC). + + +
+
+ Request Body (<literal>C</literal>) + This part contains the request body of the transaction, after dechunking and + decompression (if applicable). +
+
+ Intended Response Headers (<literal>D</literal>) + This part contains the status line and the request headers that would have + been delivered to the client had ModSecurity not intervened. Thus this part + makes sense only for transactions where ModSecurity altered the data flow. By + differentiating before the intended and the final response headers, we are able + to record what was internally ready for sending, but also what was actually + sent. + + This part is reserved for future use. It is not implemented in ModSecurity + 2.x. + +
+
+ Intended Response Body (<literal>E</literal>) + This part contains the transaction response body (before compression and + chunking, where used) that was either sent or would have been sent had + ModSecurity not intervened. You can find whether interception took place by + looking at the Action header of the part H. If that header is present, and the interception took place in + phase 3 or 4 then the E part contains the intended response + body. Otherwise, it contains the actual response body. + + Once the G (actual response body) part is implemented, + part E will be present only in audit logs that contain a + transaction that was intercepted, and there will be no need for further + analsys. + +
+
+ Response Headers (<literal>F</literal>) + This part contains the actual response headers sent to the client. Since + ModSecurity 2.x for Apache does not access the raw connection data, it + constructs part F out of the internal Apache data structures + that hold the response headers. + Some headers (the Date and Server + response headers) are generated just before they are sent and ModSecurity is not + able to record those. You should note than ModSecurity is working as part of a + reverse proxy, the backend web server will have generated these two servers, and + in that case they will be recorded. +
+
+ Response Body (G) + When implemented, this part will contain the actual response body before + compression and chunking. + + This part is reserved for future use. It is not implemented in ModSecurity + 2.x. + +
+
+ Audit Log Trailer (H) + Part H contains additional transaction meta-data that was + obtained from the web server or from ModSecurity itself. The part contains a + number of trailer headers, which are similar to HTTP headers (without support + for header folding): + + Action + + + Apache-Error + + + Message + + + Producer + + + Response-Body-Transformed + + + Sanitised-Args + + + Sanitised-Request-Headers + + + Sanitised-Response-Headers + + + Server + + + Stopwatch + + + WebApp-Info + + +
+ Action + The Action header is present only for the transactions + that were intercepted: + Action: Intercepted (phase 2) + The phase information documents the phase in which the decision to + intercept took place. +
+
+ Apache-Error + The Apache-Error header contains Apache error log messages observed by + ModSecurity, excluding those sent by ModSecurity itself. For example: + Apache-Error: [file "/tmp/buildd/apache2-2.0.54/build-tree/apache2/server/ + core.c"] [line 3505] [level 3] File does not exist: /var/www/www. + modsecurity.org/fst/documentation/modsecurity-apache/2.5.0-dev2 +
+
+ Message + Zero or more Message headers can be present in any + trailer, and each such header will represent a single ModSecurity warning or + error, displayed in the order they were raised. + The example below was broken into multiple lines to make it fit this + page: + Message: Access denied with code 400 (phase 2). Pattern match "^\w+:/" at + REQUEST_URI_RAW. [file "/etc/apache2/rules-1.6.1/modsecurity_crs_20_ + protocol_violations.conf"] [line "74"] [id "960014"] [msg "Proxy access + attempt"] [severity "CRITICAL"] [tag "PROTOCOL_VIOLATION/PROXY_ACCESS"] +
+
+ Producer + The Producer header identifies the product that + generated the audit log. For example: + Producer: ModSecurity for Apache/2.5.5 (http://www.modsecurity.org/). + ModSecurity allows rule sets to add their own signatures to the Producer information (this is done using the SecComponentSignature directive). Below is an example of the + Producer header with the signature of one component + (all one line): + Producer: ModSecurity for Apache/2.5.5 (http://www.modsecurity.org/); + MyComponent/1.0.0 (Beta). +
+
+ Response-Body-Transformed + This header will appear in every audit log that contains a response + body: + Response-Body-Transformed: Dechunked + The contents of the header is constant at present, so the header is only + useful as a reminder that the recorded response body is not identical to the + one sent to the client. The actual content is the same, except that Apache + may further compress the body and deliver it in chunks. +
+
+ Sanitised-Args + The Sanitised-Args header contains a list of arguments + that were sanitised (each byte of their content replaced with an asterisk) + before logging. For example: + Sanitised-Args: "old_password", "new_password", "new_password_repeat". +
+
+ Sanitised-Request-Headers + The Sanitised-Request-Headers header contains a list of + request headers that were sanitised before logging. For example: + Sanitised-Request-Headers: "Authentication". +
+
+ Sanitised-Response-Headers + The Sanitised-Response-Headers header contains a list + of response headers that were sanitised before logging. For example: + Sanitised-Response-Headers: "My-Custom-Header". +
+
+ Server + The Server header identifies the web server. For + example: + Server: Apache/2.0.54 (Debian GNU/Linux) mod_ssl/2.0.54 OpenSSL/0.9.7e + This information may sometimes be present in any of the parts that contain + response headers, but there are a few cases when it isn't: + + None of the response headers were recoreded. + + + The information in the response headers is not accurate + because server signature masking was used. + + +
+
+ Stopwatch + The Stopwatch header provides certain diagnostic + information that allows you to determine the performance of the web server + and of ModSecurity itself. It will typically look like this: + Stopwatch: 1222945098201902 2118976 (770* 4400 -) + Each line can contain up to 5 different values. Some values can be absent; + each absent value will be replaced with a dash. + The meanings of the values are as follows (all values are in + microseconds): + + Transaction timestamp in microseconds since January 1st, + 1970. + + + Transaction duration. + + + The time between the moment Apache started processing the + request and until phase 2 of ModSecurity began. If an asterisk + is present that means the time includes the time it took + ModSecurity to read the request body from the client (typically + slow). This value can be used to provide a rough estimate of the + client speed, but only with larger request bodies (the smaller + request bodies may arrive in a single TCP/IP packet). + + + The time between the start of processing and until phase 2 was + completed. If you substract the previous value from this value + you will get the exact duration of phase 2 (which is the main + rule processing phase). + + + The time between the start of request processing and util we + began sending a fully-buffered response body to the client. If + you substract this value from the total transaction duration and + divide with the response body size you may get a rough estimate + of the client speed, but only for larger response bodies. + + +
+
+ WebApp-Info + The WebApp-Info header contains information on the + application to which the recorded transaction belongs. This information will + appear only if it is known, which will happen if SecWebAppId was set, or setsid or setuid executed in the transaction. + The header uses the following format: + WebApp-Info: "WEBAPPID" "SESSIONID" "USERID" + Each unknown value is replaced with a dash. +
+
+
+ Reduced Multipart Request Body (<literal>I</literal>) + Transactions that deal with file uploads tend to be large, yet the file + contents is not always relevant from the security point of view. The I part was designed to avoid recording raw multipart/form-data request bodies, replacing them with a + simulated application/x-www-form-urlencoded body that + contains the same key-value parameters. + The reduced multipart request body will not contain any file information. The + J part (currently not implemented) is intended to carry + the file metadata. +
+
+ Multipart Files Information (<literal>J</literal>) + The purpose of part J is to record the information on the + files contained in a multipart/form-data request body. This + is handy in the cases when the original request body was not recorded, or when + only a reduced version was recorded (e.g. when part I was + used instead of part C). + + This part is reserved for future use. It is not implemented in ModSecurity + 2.x. + +
+
+ Matched Rules (<literal>K</literal>) + The matched rules part contains a record of all ModSecurity rules that matched + during transaction processing. You should note that if a rule that belongs to a + chain matches then the entire chain will be recorded. This is because, even + though the disruptive action may not have executed, other per-rule actions have, + and you will need to see the entire chain in order to understand the + rules. + This part is available starting with ModSecurity 2.5.x. +
+
+ Audit Log Footer (<literal>Z</literal>) + Part Z is a special part that only has a boundary but no + content. Its only purpose is to signal the end of an audit log. +
+
+
+ Storage Formats + ModSecurity supports two audit log storage formats: + + Serial audit log format - multiple audit log + files stored in the same file. + + + Concurrent audit log format - one file is used + for every audit log. + + +
+ Serial Audit Log Format + The serial audit log format stores multiple audit log entries within the same + file (one after another). This is often very convinent (audit log entries are + easy to find) but this format is only suitable for light logging in the current + ModSecurity implementation because writing to the file is serialised: only one + audit log entry can be written at any one time. +
+
+ Concurrent Audit Log Format + The concurrent audit log format uses one file per audit log entry, and allows + many transactions to be recorded at once. A hierarchical directory structure is + used to ensure that the number of files created in any one directory remains + relatively small. For example: + $LOGGING-HOME/20081128/20081128-1414/20081128-141417- + egDKy38AAAEAAAyMHXsAAAAA + The current time is used to work out the directory structure. The file name is + constructed using the current time and the transaction ID. + The creation of every audit log in concurrent format is recorded with an entry + in the concurrent audit log index file. The format of each + line resembles the common web server access log format. For example: + 192.168.0.111 192.168.0.1 - - [28/Nov/2008:15:06:32 +0000] + "GET /?p=\\ HTTP/1.1" 200 69 "-" "-" NOfRx38AAAEAAAzcCU4AAAAA + "-" /20081128/20081128-1506/20081128-150632-NOfRx38AAAEAAAzcCU4AAAAA + 0 1183 md5:ffee2d414cd43c2f8ae151652910ed96 + The tokens on the line are as follows: + + + Hostname (or IP address, if the hostname is not known) + + + Source IP address + + + Remote user (from HTTP Authentication) + + + Local user (from identd) + + + Timestamp + + + Request line + + + Response status + + + Bytes sent (in the response body) + + + Referrer information + + + User-Agent information + + + Transaction ID + + + Session ID + + + Audit log file name (relative to the audit logging home, as configured + using the SecAuditLogStorageDir directive) + + + Audit log offset + + + Audit log size + + + Audit log hash (the has begins with the name of the algorithm used, + followed by a colon, followed by the hexadecimal representation of the + hash itself); this hash can be used to verify that the transaction was + correctly recorded and that it hasn't been modified since. + + + + Lines in the index file will be up to 3980 bytes long, and the information + logged will be reduced to fit where necessary. Reduction will occur within + the individual fields, but the overall format will remain the same. The + character L will appear as the last character on a + reduced line. A space will be the last character on a line that was not + reduced to stay within the limit. + +
+
+
+ Transport Protocol + Audit logs generated in multi-sensor deployments are of little use if left on the + sensors. More commonly, they will be transported to a central logging server using + the transport protocol described in this section: + + + The transport protocol is based on the HTTP protocol. + + + The server end is an SSL-enabled web server with HTTP Basic Authentication + configured. + + + Clients will open a connection to the centralisation web server and + authenticate (given the end-point URI, the username and the + password). + + + Clients will submit every audit log in a single PUT + transaction, placing the file in the body of the request and additional + information in the request headers (see below for details). + + + Server will process each submission and respond with an appropriate status + code: + + + 200 (OK) - the submission was processed; the client can delete the + corresponding audit log entry if it so desires. The same audit log + entry must not be submitted again. + + + 409 (Conflict) - if the submission is in invalid format and cannot + be processed. The client should attempt to fix the problem with the + submission and attempt delivery again at a later time. This error is + generally going to occur due to a programming error in the protocol + implementation, and not because of the content of the audit log + entry that is being transported. + + + 500 (Internal Server Error) - if the server was unable to + correctly process the submission, due to its own fault. The client + should re-attempt delivery at a later time. A client that starts + receiving 500 reponses to all its submission should suspend its + operations for a period of time before continuing. + + + + + + Server implementations are advised to accept all submissions that correctly + implement the protocol. Clients are unlikely to be able to overcome problems + within audit log entries, so such problems are best resolved on the server + side. + + + When en error occurs, the server may place an explanation of the problem in + the text part of the response line. + +
+ Request Headers Information + Each audit log entry submission must contain additional information in the + request headers: + + + Header X-Content-Hash must contain the audit log + entry hash. Clients should expect the audit log entries to be validated + against the hash by the server. + + + Header X-ForensicLog-Summary must contain the + entire concurrent format index line. + + + The Content-Lenght header must be present and + contain the length of the audit log entry. + + +
+
+
+
diff --git a/2.5.13/2.5.x/doc/pdf.xsl b/2.5.13/2.5.x/doc/pdf.xsl new file mode 100644 index 00000000..b7b70c17 --- /dev/null +++ b/2.5.13/2.5.x/doc/pdf.xsl @@ -0,0 +1,166 @@ + + + + + + + + + + + +0pt +11 +1.5 +1 20 1 + + + 0.0em + 0.0em + 0.0em + + 0.2em + 0.2em + 0.2em + + + + + + + + bold + always + left + 1.0em + 1.0em + 1.0em + 0.0em + 0.0em + 0.0em + + + + 24pt + + page + + 0.0em + 0.0em + 0.0em + + + + + + 18pt + + + + 14pt + + + + 12pt + + + + 0.0em + 0.0em + 0.0em + 0.2em + 0.2em + 0.2em + + + + 0.2em + 0.2em + 0.2em + + 0.2em + 0.2em + 0.2em + + 2pc + + + + + #f0f0f0 + 2pt + + + + 90% + + + + + + + + + + + + + + + + + + + + + + + + ​ + + + + + diff --git a/2.5.13/2.5.x/doc/trustwave-logo-small.gif b/2.5.13/2.5.x/doc/trustwave-logo-small.gif new file mode 100644 index 0000000000000000000000000000000000000000..e88043092bd67f6824450b72f826d1b21a2e2b95 GIT binary patch literal 4654 zcmWlYc|6pK8^^!LT;`@R&G(vZs9j)|>o*M4V^QMQH(g>4*Z zNOoMC3j1}HY{yu#!rF1H^;6lgK^JZpd_GHvm^}QILd=Fkic3yti-QB&r z{EB99aPs5)|Ni&C_V)I5w~kIuP7VwVeE2YNp-K7j<;&bBZvsk^qO%&_zJ0sAyySSc zjC=GhJ0q&=-O%LRN0Zp1&({{0J}DFmAmkQ?VXdvLwXcWrI_h8iF&JucQ5;s;f&&OQlcyA3b{X z_dg$FavB$xmM^!dCT6BOIyxRacmRZErT<#f`(YH?b8Bv4>Erwo)-`gowsU@IQIz$| z_($#2k+;!5XMXk%1ZUoQ{a9M|QZxSV^xPsClIe1xJhkFAkg~I=tI4I{EbTyUNq6hq z+#DF1WgDlAD`*8mvPP#S{`hB1_v-!)r%Jb_)XXg|e5Prr8wkndWyM0WAnokO&o6#G zHbCB=Q}OGc*73^jkqNg8D#IiBvkPMC=6kUKqGPiuMvKALF zE-$_Rch>quQT<@2buPs?@WK3%VJREV+nsW$(<#DUEi#d+42M_SX*| zKE#LIgo3lj$KQSC{kwMzc?1xe35I444Gj;E{xvc((%0A5-`Cgt`)FJ5*q5kkM_R4r zN#*?9?492lzPN|J(!3j)`1semsjjXrQT2^apFR=8Z?DOW;m4P}9n~JY)BJH^e(dkR zKQ1nqAHTQZbXjR>DMOUk)YyCn`~B>_XY&h7)6>)1xoP#E@1~}vx<)2=^Elu?Z2y!0 zIsuSY0FY$jr&2T~l2-BewW;!(QLjOGfn$kD- z!>VHcXs@~d`%j|g)g}T2@PKeYcCc~B``$|HKM$`LiZJvctS;4;d!h$`Xd1z8@yrI4 zb?JBhgq840|NXD$Gd6`|Cp3oq5tE$6ZC4L(mTWxe>f@@Gv#iNVS(EGKFD{fFcye`Z zQ~A@U7XrU5=WFax_k=fWK#M(3GBL=oIndx9U=Dq!Oxj(f?3ng>Q(~jHDS7e)lw38i z@>Mu5tikT*xfV^ZhqwM=L7T-BM}qXi;=%F072pb15NwV!;4;B%i4^_7#nJ)1?b?(t zzN20Wj`UFjEv_@~RjGEqOB!E%aJBs_qH99jnv1g+&=j_Hf&{xls#9$MT(9HuMDgK|g!Xnfg{y z0d&l|f4_XAHg&@>y23m9JC;0!4KRhF%GBak_MQfCCNu*2%R94VFnz_y0F>46k^QUd zt?%WYQptS)TVY{_E@>g(U_GAi2-w$MbY54*PA*W+{vLYxL3@{&;p{c8nV1lc#WsG5} zUb=0Da{m>q>Gj(0Mka;)VN8SRj{`nJ?s0c@skX4ievFrj!N*SVx;Nak$7uB0qnGDtvpR|leXE#RHI&ilYO#rkOqiIkQk!Q zh9DCoBkERe4CWE4JV+Q#@aCF&6x!-R4)#kn&1`i%0v;pTBZF=YLbmS$J)s#y*<_^7W#d7d(4~pi zRv;BALENY)>8>C+%SnS?4C7L4*QqJd(qQ@2`za*=~!`4r#Yq zt=5zn26=+7Fi&<|Bi*^kV$)3}!Ri&ja1irA!C2CRAcAG)Avx_NTn9{BAPQcFBYum*?jnhv2jZ)LwecEJ6licsLdPjxhD)&L< zPBB2ZxEa`7D=~b=eBLf%ThiDLzy)~=rysn!^F2!D*Lu3P{QbHIWF_;bF=0?55>C1v z*xH%ACIpo2)e1TLh3J|Fpw$0^B=gX3u;qjb0E0ln>3HFvuehj_<^?F*lf;|Wko=WW zxJ@k)Ao)amwUm+o4DtpvB-8rV@4YHO}tbz$qlVCs$TmRV{W}2kMZFf=NQe-$&mL}Dg zG}y?zDBy56WjMd-{`MpUGndsrY~XUK=SUd4Ae{hbo9PLx8c!1AJpU@zk5%IbBKW)g zzf=xW61h!oTO+US?P)gH)n-*Znu#;D!f+)nILp=8HT*P}ZK=RHBncAz%mMfj3C|f5 zb>;B$fgLe;f{7MAFviuvhiSk02^)2)nqS#0U6PgCou{$O%&6zpDQjX7Qs*Y{@XVa# zWxFa$Ql0Q9wq|?Uu}FhmMz0x;vW@Nu_Q|2uX0(%YDe}e#U&Nds(m2<&n%Ojm(tu_}Q@mE@{n_tKuZ8-o<3R24)wk(xf5R$A)GQ?}agHq2R%!Jc5+E-{z0id3w#V=$G0 znH~CJ7&YUYg`e)t^s8Cv@!+J%rN(tqp_X-$cSZ|e$mUW8oHo`TIB#SAEsQ_cmeTM= z7`Wfy8p&-g$zWQNX{ZI6(;7HoMU;f-tkOT&ikS=0WEN9FOA?&W@f#!n2`orQ9t3Vj zepu1t>bb;t>$EGqmUD5I`<;QdfuDu)wsm~gKO20T}WeM_CJ^LUjfo} zO9$&>CLNZ4X9L=Iybxx#9 zf}|B4&vakcG~6brLO?06fIa3ur(OoFagvZS^OlSXnA0$u$20bi#NGNk%2tTTD$dn% z3GM*SObyWFq*OKWsUzvBfRW54hlud0D%eU3O@Q1Q0V9=5o&Z*Rh)GNY(oyWN-KBF4 z0G|u!UP3^rr6|lVr0U@}`;T>Y=*;8HG zJsVpE<-E`s;zGNbL3L29C#5NcS_;G=oOZ9-BLivNcI97o|KF1!xq z(}DPyjMZtU-*rXsN_qvr~UXEAE9oEVI5Sa9&q!x&3kVzDP5 zkCJx+^5e|lm~>)|gPyY%K97JGBiW893ZkTtR)=soAsiu2X=tQKc43eWb%$%Y-9 z0R`mD#M=&W@EUxioFGFID6mqel|(Fk) zgUba*%%1?tUGq|K-Co#91M#@vq#EqU3W))g{6P+1&Bzeq>;rHc!>gQDzNyM!!WN;x z@NpOIw?s!Mq->cRJFL9_Zy*!rUWe z<_9qEe|G$Y`4iLHH?Hetk-1%ZvO|)AoH{U literal 0 HcmV?d00001 diff --git a/2.5.13/2.5.x/modsecurity.conf-minimal b/2.5.13/2.5.x/modsecurity.conf-minimal new file mode 100644 index 00000000..091da85d --- /dev/null +++ b/2.5.13/2.5.x/modsecurity.conf-minimal @@ -0,0 +1,76 @@ + +# Basic configuration options +SecRuleEngine On +SecRequestBodyAccess On +SecResponseBodyAccess Off + +# PCRE Tuning +SecPcreMatchLimit 1000 +SecPcreMatchLimitRecursion 1000 + +# Handling of file uploads +# TODO Choose a folder private to Apache. +# SecUploadDir /opt/apache-frontend/tmp/ +SecUploadKeepFiles Off +SecUploadFileLimit 10 + +# Debug log +SecDebugLog logs/modsec_debug.log +SecDebugLogLevel 0 + +# Serial audit log +SecAuditEngine RelevantOnly +SecAuditLogRelevantStatus ^5 +SecAuditLogParts ABIFHZ +SecAuditLogType Serial +SecAuditLog logs/modsec_audit.log + +# Maximum request body size we will +# accept for buffering +SecRequestBodyLimit 131072 + +# Store up to 128 KB in memory +SecRequestBodyInMemoryLimit 131072 + +# Buffer response bodies of up to +# 512 KB in length +SecResponseBodyLimit 524288 + +# Verify that we've correctly processed the request body. +# As a rule of thumb, when failing to process a request body +# you should reject the request (when deployed in blocking mode) +# or log a high-severity alert (when deployed in detection-only mode). +SecRule REQBODY_PROCESSOR_ERROR "!@eq 0" \ +"phase:2,t:none,log,deny,msg:'Failed to parse request body.',severity:2" + +# By default be strict with what we accept in the multipart/form-data +# request body. If the rule below proves to be too strict for your +# environment consider changing it to detection-only. You are encouraged +# _not_ to remove it altogether. +SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ +"phase:2,t:none,log,deny,msg:'Multipart request body \ +failed strict validation: \ +PE %{REQBODY_PROCESSOR_ERROR}, \ +BQ %{MULTIPART_BOUNDARY_QUOTED}, \ +BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ +DB %{MULTIPART_DATA_BEFORE}, \ +DA %{MULTIPART_DATA_AFTER}, \ +HF %{MULTIPART_HEADER_FOLDING}, \ +LF %{MULTIPART_LF_LINE}, \ +SM %{MULTIPART_SEMICOLON_MISSING}, \ +IQ %{MULTIPART_INVALID_QUOTING}, \ +IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ +IH %{MULTIPART_FILE_LIMIT_EXCEEDED}'" + +# Did we see anything that might be a boundary? +SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \ +"phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'" + +# Some internal errors will set flags in TX and we will need to look for these. +# All of these are prefixed with "MSC_". The following flags currently exist: +# +# MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded. +# +SecRule TX:/^MSC_/ "!@streq 0" \ + "phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'" + diff --git a/2.5.13/2.5.x/tools/README b/2.5.13/2.5.x/tools/README new file mode 100644 index 00000000..cab5dd64 --- /dev/null +++ b/2.5.13/2.5.x/tools/README @@ -0,0 +1,6 @@ +These tools are built during the ModSecurity configure process run under the +apache2 directory. To use them you will first need to run configure under +the apache2 directory: + +./configure [any options] + diff --git a/2.5.13/2.5.x/tools/rules-updater-example.conf b/2.5.13/2.5.x/tools/rules-updater-example.conf new file mode 100644 index 00000000..4fa5faeb --- /dev/null +++ b/2.5.13/2.5.x/tools/rules-updater-example.conf @@ -0,0 +1,23 @@ +# This is an example configuration to be used with ruleset-updator.pl -c + +# The repository URI. +RepositoryURI http://username:password@www.example.tld/repository/ + +# Where to download the rulesets to +LocalRepository /path/to/repository + +# Where to unpack the rulesets (if Unpack is true) +LocalRules /path/to/repository + +# What version (or version prefix) to use +#Version 1.5 + +# Should we unpack the ruleset to LocalRules? +Unpack True + +# Email update notifications +#NotifyEmail "modsec-admin@example.tld, someone@example.tld" +#NotifyEmailFrom "ModSec Rules Updater " + +# Output lots of debugging info? +Debug False diff --git a/2.5.13/2.5.x/tools/rules-updater.pl.in b/2.5.13/2.5.x/tools/rules-updater.pl.in new file mode 100644 index 00000000..16b13638 --- /dev/null +++ b/2.5.13/2.5.x/tools/rules-updater.pl.in @@ -0,0 +1,454 @@ +#!@PERL@ +# +# Fetches the latest ModSecurity Ruleset +# + +use strict; +use Sys::Hostname; +use LWP::UserAgent (); +use LWP::Debug qw(-); +use URI (); +use HTTP::Date (); +use Cwd qw(getcwd); +use Getopt::Std; + +my $VERSION = "0.0.1"; +my($SCRIPT) = ($0 =~ m/([^\/\\]+)$/); +my $CRLFRE = qr/\015?\012/; +my $HOST = Sys::Hostname::hostname(); +my $UNZIP = [qw(unzip -a)]; +my $SENDMAIL = [qw(/usr/lib/sendmail -oi -t)]; +my $HAVE_GNUPG = 0; +my %PREFIX_MAP = ( + -dev => 0, + -rc => 1, + "" => 9, +); +my %GPG_TRUST = (); +my $REQUIRED_SIG_TRUST; + +eval "use GnuPG qw(:trust)"; +if ($@) { + warn "Could not load GnuPG module - cannot verify ruleset signatures\n"; +} +else { + $HAVE_GNUPG = 1; + %GPG_TRUST = ( + &TRUST_UNDEFINED => "not", + &TRUST_NEVER => "not", + &TRUST_MARGINAL => "marginally", + &TRUST_FULLY => "fully", + &TRUST_ULTIMATE => "ultimatly", + ); + $REQUIRED_SIG_TRUST = &TRUST_FULLY; +} + +################################################################################ +################################################################################ + +my @fetched = (); +my %opt = (); +getopts('c:r:p:s:v:t:e:f:EuS:D:R:U:F:ldh', \%opt); + +usage(1) if(defined $opt{h}); +usage(1) if(@ARGV > 1); + +# Make sure we have an action +if (! grep { defined } @opt{qw(S D R U F l)}) { + usage(1, "Action required."); +} + +# Merge config with commandline opts +if ($opt{c}) { + %opt = parse_config($opt{c}, \%opt); +} + +LWP::Debug::level("+") if ($opt{d}); + +# Make the version into a regex +if (defined $opt{v}) { + my($a,$b,$c,$d) = ($opt{v} =~ m/^(\d+)\.?(\d+)?\.?(\d+)?(?:-(\D+\d+$)|($))/); + if (defined $d) { + (my $key = $d) =~ s/^(\D+)\d+$/-$1/; + unless (exists $PREFIX_MAP{$key}) { + usage(1, "Invalid version (bad suffix \"$d\"): $opt{v}"); + } + $opt{v} = qr/^$a\.$b\.$c-$d$/; + } + elsif (defined $c) { + $opt{v} = qr/^$a\.$b\.$c(?:-|$)/; + } + elsif (defined $b) { + $opt{v} = qr/^$a\.$b\./; + } + elsif (defined $a) { + $opt{v} = qr/^$a\./; + } + else { + usage(1, "Invalid version: $opt{v}"); + } + if ($opt{d}) { + print STDERR "Using version regex: $opt{v}\n"; + } +} +else { + $opt{v} = qr/^/; +} + +# Remove trailing slashes from uri and path +$opt{r} =~ s/\/+$//; +$opt{p} =~ s/\/+$//; + +# Required opts +usage(1, "Repository (-r) required.") unless(defined $opt{r}); +usage(1, "Local path (-p) required.") unless(defined $opt{p} or defined $opt{l}); + +my $ua = LWP::UserAgent->new( + agent => "ModSecurity Updator/$VERSION", + keep_alive => 1, + env_proxy => 1, + max_redirect => 5, + requests_redirectable => [qw(GET HEAD)], + timeout => ($opt{t} || 600), +); + +sub usage { + my $rc = defined($$_[0]) ? $_[0] : 0; + my $msg = defined($_[1]) ? "\n$_[1]\n\n" : ""; + + print STDERR << "EOT"; +${msg}Usage: $SCRIPT [-c config_file] [[options] [action] + + Options (commandline will override config file): + -r uri RepositoryURI Repository URI. + -p path LocalRepository Local repository path to use as base for downloads. + -s path LocalRules Local rules base path to use for unpacking. + -v text Version Full/partial version (EX: 1, 1.5, 1.5.2, 1.5.2-dev3) + -t secs Timeout Timeout for fetching data in seconds (default 600). + -e addr NotifyEmail Notify via email on update (comma separated list). + -f addr NotifyEmailFrom From address for notification email. + -u Unpack Unpack into LocalRules/version path. + -d Debug Print out lots of debugging. + + Actions: + -S name Fetch the latest stable ruleset, "name" + -D name Fetch the latest development ruleset, "name" + -R name Fetch the latest release candidate ruleset, "name" + -U name Fetch the latest unstable (non-stable) ruleset, "name" + -F name Fetch the latest ruleset, "name" + -l Print listing of what is available + + Misc: + -c Specify a config file for options. + -h This help + +Examples: + +# Get a list of what the repository contains: +$SCRIPT -rhttp://host/repo/ -l + +# Get a partial list of versions 1.5.x: +$SCRIPT -rhttp://host/repo/ -v1.5 -l + +# Get the latest stable version of "breach_ModSecurityCoreRules": +$SCRIPT -rhttp://host/repo/ -p/my/repo -Sbreach_ModSecurityCoreRules + +# Get the latest stable 1.5 release of "breach_ModSecurityCoreRules": +$SCRIPT -rhttp://host/repo/ -p/my/repo -v1.5 -Sbreach_ModSecurityCoreRules +EOT + exit $rc; +} + +sub sort_versions { + (my $A = $a) =~ s/^(\d+)\.(\d+)\.(\d+)(-[^-\d]+|)(\d*)$/sprintf("%03d%03d%03d%03d%03d", $1, $2, $3, $PREFIX_MAP{$4}, $5)/e; + (my $B = $b) =~ s/^(\d+)\.(\d+)\.(\d+)(-[^-\d]+|)(\d*)$/sprintf("%03d%03d%03d%03d%03d", $1, $2, $3, $PREFIX_MAP{$4}, $5)/e; + return $A cmp $B; +} + +sub parse_config { + my($file,$clo) = @_; + my %cfg = (); + + print STDERR "Parsing config: $file\n" if ($opt{d}); + open(CFG, "<$file") or die "Failed to open config \"$file\": $!\n"; + while() { + # Skip comments and empty lines + next if (/^\s*(?:#|$)/); + + # Parse + chomp; + my($var,$q1,$val,$q2) = (m/^\s*(\S+)\s+(['"]?)(.*)(\2)\s*$/); + + # Fixup values + $var = lc($var); + if ($val =~ m/^(?:true|on)$/i) { $val = 1 }; + if ($val =~ m/^(?:false|off)$/i) { $val = 0 }; + + # Set opts + if ($var eq "repositoryuri") { $cfg{r} = $val } + elsif ($var eq "localrepository") { $cfg{p} = $val } + elsif ($var eq "localrules") { $cfg{s} = $val } + elsif ($var eq "version") { $cfg{v} = $val } + elsif ($var eq "timeout") { $cfg{t} = $val } + elsif ($var eq "notifyemail") { $cfg{e} = $val } + elsif ($var eq "notifyemailfrom") { $cfg{f} = $val } + elsif ($var eq "notifyemaildiff") { $cfg{E} = $val } + elsif ($var eq "unpack") { $cfg{u} = $val } + elsif ($var eq "debug") { $cfg{d} = $val } + else { die "Invalid config directive: $var\n" } + } + close CFG; + + my($k, $v); + while (($k, $v) = each %{$clo || {}}) { + $cfg{$k} = $v if (defined $v); + } + + return %cfg; +} + +sub repository_dump { + my @replist = repository_listing(); + + print STDERR "\nRepository: $opt{r}\n\n"; + unless (@replist) { + print STDERR "No matching entries.\n"; + return; + } + + for my $repo (@replist) { + print "$repo {\n"; + my @versions = ruleset_available_versions($repo); + for my $version (@versions) { + if ($version =~ m/$opt{v}/) { + printf "%15s: %s_%s.zip\n", $version, $repo, $version; + } + elsif ($opt{d}) { + print STDERR "Skipping version: $version\n"; + } + } + print "}\n"; + } +} + +sub repository_listing { + my $res = $ua->get("$opt{r}/.listing"); + unless ($res->is_success()) { + die "Failed to get repository listing \"$opt{r}/.listing\": ".$res->status_line()."\n"; + } + return grep(/\S/, split(/$CRLFRE/, $res->content)) ; +} + +sub ruleset_listing { + my $res = $ua->get("$opt{r}/$_[0]/.listing"); + unless ($res->is_success()) { + die "Failed to get ruleset listing \"$opt{r}/$_[0]/.listing\": ".$res->status_line()."\n"; + } + return grep(/\S/, split(/$CRLFRE/, $res->content)) ; +} + +sub ruleset_available_versions { + return sort sort_versions map { m/_([^_]+)\.zip.*$/; $1 } ruleset_listing($_[0]); +} + +sub ruleset_fetch { + my($repo, $version) = @_; + + # Create paths + if (! -e "$opt{p}" ) { + mkdir "$opt{p}" or die "Failed to create \"$opt{p}\": $!\n"; + } + if (! -e "$opt{p}/$repo" ) { + mkdir "$opt{p}/$repo" or die "Failed to create \"$opt{p}/$repo\": $!\n"; + } + + my $fn = "${repo}_$version.zip"; + my $ruleset = "$repo/$fn"; + my $ruleset_sig = "$repo/$fn.sig"; + + if (-e "$opt{p}/$ruleset") { + die "Refused to overwrite ruleset \"$opt{p}/$ruleset\".\n"; + } + + # Fetch the ruleset + print STDERR "Fetching: $ruleset ...\n"; + my $res = $ua->get( + "$opt{r}/$ruleset", + ":content_file" => "$opt{p}/$ruleset", + ); + die "Failed to retrieve ruleset $ruleset: ".$res->status_line()."\n" unless ($res->is_success()); + + # Fetch the ruleset signature + if (-e "$opt{p}/$ruleset_sig") { + die "Refused to overwrite ruleset signature \"$opt{p}/$ruleset_sig\".\n"; + } + $res = $ua->get( + "$opt{r}/$ruleset_sig", + ":content_file" => "$opt{p}/$ruleset_sig", + ); + + # Verify the signature if we can + if ($HAVE_GNUPG) { + die "Failed to retrieve ruleset signature $ruleset_sig: ".$res->status_line()."\n" unless ($res->is_success()); + + ruleset_verifysig("$opt{p}/$ruleset", "$opt{p}/$ruleset_sig"); + } + push @fetched, [$repo, $version, $ruleset, undef]; +} + +sub ruleset_unpack { + my($repo, $version, $ruleset) = @{ $_[0] || [] }; + my $fn = "$opt{p}/$ruleset"; + + if (! -e "$fn" ) { + die "Internal Error: No ruleset to unpack - \"$fn\"\n"; + } + + # Create paths + if (! -e "$opt{s}" ) { + mkdir "$opt{s}" or die "Failed to create \"$opt{p}\": $!\n"; + } + if (! -e "$opt{s}/$repo" ) { + mkdir "$opt{s}/$repo" or die "Failed to create \"$opt{p}/$repo\": $!\n"; + } + if (! -e "$opt{s}/$repo/$version" ) { + mkdir "$opt{s}/$repo/$version" or die "Failed to create \"$opt{p}/$repo/$version\": $!\n"; + } + else { + die "Refused to overwrite previously unpacked \"$opt{s}/$repo/$version\".\n"; + } + + # TODO: Verify sig + + my $pwd = getcwd(); + my $unpackdir = "$opt{s}/$repo/$version"; + chdir "$unpackdir"; + if ($@) { + my $err = $!; + chdir $pwd; + die "Failed to chdir to \"$unpackdir\": $err\n"; + } + undef $!; + system(@$UNZIP, $fn); + if ($? != 0) { + my $err = $!; + chdir $pwd; + die "Failed to unpack \"$unpackdir\"".($err?": $err":".")."\n"; + } + chdir $pwd; + + # Add where we unpacked it + $_->[3] = $unpackdir; + + return 0; +} + +sub ruleset_fetch_latest { + my($repo, $type) = @_; + my @versions = ruleset_available_versions($repo); + my $verre = defined($opt{v}) ? qr/^$opt{v}/ : qr/^/; + my $typere = undef; + + # Figure out what to look for + if (defined($type) and $type ne "") { + if ($type eq "UNSTABLE") { + $typere = qr/\d-\D+\d+$/; + } + else { + $typere = qr/\d-$type\d+$/; + } + } + elsif (defined($type)) { + qr/\.\d+$/; + } + + while (@versions) { + my $last = pop(@versions); + # Check REs on version + if ($last =~ m/$opt{v}/ and (!defined($typere) || $last =~ m/$typere/)) { + return ruleset_fetch($repo, $last); + } + if ($opt{d}) { + print STDERR "Skipping version: $last\n"; + } + } + + die "No $type ruleset found.\n"; +} + +sub notify_email { + my $version_text = join("\n", map { "$_->[0] v$_->[1]".(defined($_->[3])?": $_->[3]":"") } @_); + my $from = $opt{f} ? "From: $opt{f}\n" : ""; + my $body = << "EOT"; +ModSecurity rulesets updated and ready to install on host $HOST: + +$version_text + +ModSecurity - http://www.modsecurity.org/ +EOT + + # TODO: Diffs + + open(SM, "|-", @$SENDMAIL) or die "Failed to send mail: $!\n"; + print STDERR "Sending notification email to: $opt{e}\n"; + print SM << "EOT"; +${from}To: $opt{e} +Subject: [$HOST] ModSecurity Ruleset Update Notification + +$body +EOT + close SM; +} + +sub ruleset_verifysig { + my($fn, $sigfn) = @_; + + print STDERR "Verifying \"$fn\" with signature \"$sigfn\"\n"; + my $gpg = new GnuPG(); + my $sig = eval { $gpg->verify( signature => $sigfn, file => $fn ) }; + if (defined $sig) { + print STDERR sig2str($sig)."\n"; + } + if (!defined($sig)) { + die "Signature validation failed.\n"; + } + if ( $sig->{trust} < $REQUIRED_SIG_TRUST ) { + die "Signature is not trusted ".$GPG_TRUST{$REQUIRED_SIG_TRUST}.".\n"; + } + + return; +} + +sub sig2str { + my %sig = %{ $_[0] || {} }; + "Signature made ".localtime($sig{timestamp})." by $sig{user} (ID: $sig{keyid}) and is $GPG_TRUST{$sig{trust}} trusted."; +} + +################################################################################ +################################################################################ + +# List what is there +if ($opt{l}) { repository_dump(); exit 0 } +# Latest stable +elsif (defined($opt{S})) { ruleset_fetch_latest($opt{S}, "") } +# Latest development +elsif (defined($opt{D})) { ruleset_fetch_latest($opt{D}, "dev") } +# Latest release candidate +elsif (defined($opt{R})) { ruleset_fetch_latest($opt{R}, "rc") } +# Latest unstable +elsif (defined($opt{U})) { ruleset_fetch_latest($opt{U}, "UNSTABLE") } +# Latest (any type) +elsif (defined($opt{F})) { ruleset_fetch_latest($opt{F}, undef) } + +# Unpack +if ($opt{u}) { + if (! defined $opt{s} ) { usage(1, "LocalRules is required for unpacking.") } + for (@fetched) { + ruleset_unpack($_); + } +} + +# Unpack +if ($opt{e}) { + notify_email(@fetched); +}