diff --git a/Makefile.am b/Makefile.am index 3a0e59ba..de5839ae 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,6 +11,7 @@ MAINTAINERCLEANFILES += $(CLEANFILES) \ aclocal.m4 \ alp2/Makefile.in \ apache2/Makefile.in \ + java/Makefile.in \ build/config.guess \ build/config.sub \ build/depcomp \ diff --git a/build/ac_prog_javac.m4 b/build/ac_prog_javac.m4 new file mode 100644 index 00000000..ad6cc48e --- /dev/null +++ b/build/ac_prog_javac.m4 @@ -0,0 +1,45 @@ +dnl @synopsis AC_PROG_JAVAC +dnl +dnl AC_PROG_JAVAC tests an existing Java compiler. It uses the +dnl environment variable JAVAC then tests in sequence various common +dnl Java compilers. For political reasons, it starts with the free +dnl ones. +dnl +dnl If you want to force a specific compiler: +dnl +dnl - at the configure.in level, set JAVAC=yourcompiler before calling +dnl AC_PROG_JAVAC +dnl +dnl - at the configure level, setenv JAVAC +dnl +dnl You can use the JAVAC variable in your Makefile.in, with @JAVAC@. +dnl +dnl *Warning*: its success or failure can depend on a proper setting of +dnl the CLASSPATH env. variable. +dnl +dnl TODO: allow to exclude compilers (rationale: most Java programs +dnl cannot compile with some compilers like guavac). +dnl +dnl Note: This is part of the set of autoconf M4 macros for Java +dnl programs. It is VERY IMPORTANT that you download the whole set, +dnl some macros depend on other. Unfortunately, the autoconf archive +dnl does not support the concept of set of macros, so I had to break it +dnl for submission. The general documentation, as well as the sample +dnl configure.in, is included in the AC_PROG_JAVA macro. +dnl +dnl @category Java +dnl @author Stephane Bortzmeyer +dnl @version 2000-07-19 +dnl @license GPLWithACException + +AC_DEFUN([AC_PROG_JAVAC],[ +AC_REQUIRE([AC_EXEEXT])dnl +if test "x$JAVAPREFIX" = x; then + test "x$JAVAC" = x && AC_CHECK_PROGS(JAVAC, "gcj$EXEEXT -C" guavac$EXEEXT jikes$EXEEXT javac$EXEEXT) +else + test "x$JAVAC" = x && AC_CHECK_PROGS(JAVAC, "gcj$EXEEXT -C" guavac$EXEEXT jikes$EXEEXT javac$EXEEXT, $JAVAPREFIX) +fi +test "x$JAVAC" = x && AC_MSG_WARN([no acceptable Java compiler found in \$PATH]) +AC_PROG_JAVAC_WORKS +AC_PROVIDE([$0])dnl +]) diff --git a/build/ac_prog_javac_works.m4 b/build/ac_prog_javac_works.m4 new file mode 100644 index 00000000..22dfcb42 --- /dev/null +++ b/build/ac_prog_javac_works.m4 @@ -0,0 +1,36 @@ +dnl @synopsis AC_PROG_JAVAC_WORKS +dnl +dnl Internal use ONLY. +dnl +dnl Note: This is part of the set of autoconf M4 macros for Java +dnl programs. It is VERY IMPORTANT that you download the whole set, +dnl some macros depend on other. Unfortunately, the autoconf archive +dnl does not support the concept of set of macros, so I had to break it +dnl for submission. The general documentation, as well as the sample +dnl configure.in, is included in the AC_PROG_JAVA macro. +dnl +dnl @category Java +dnl @author Stephane Bortzmeyer +dnl @version 2000-07-19 +dnl @license GPLWithACException + +AC_DEFUN([AC_PROG_JAVAC_WORKS],[ +AC_CACHE_CHECK([if $JAVAC works], ac_cv_prog_javac_works, [ +JAVA_TEST=Test.java +CLASS_TEST=Test.class +cat << \EOF > $JAVA_TEST +/* [#]line __oline__ "configure" */ +public class Test { +} +EOF +if AC_TRY_COMMAND($JAVAC $JAVACFLAGS $JAVA_TEST) >/dev/null 2>&1; then + ac_cv_prog_javac_works=yes +else + AC_MSG_ERROR([The Java compiler $JAVAC failed (see config.log, check the CLASSPATH?)]) + echo "configure: failed program was:" >&AC_FD_CC + cat $JAVA_TEST >&AC_FD_CC +fi +rm -f $JAVA_TEST $CLASS_TEST +]) +AC_PROVIDE([$0])dnl +]) \ No newline at end of file diff --git a/build/ax_jni_include_dir.m4 b/build/ax_jni_include_dir.m4 new file mode 100644 index 00000000..ab4e51c3 --- /dev/null +++ b/build/ax_jni_include_dir.m4 @@ -0,0 +1,129 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_JNI_INCLUDE_DIR +# +# DESCRIPTION +# +# AX_JNI_INCLUDE_DIR finds include directories needed for compiling +# programs using the JNI interface. +# +# JNI include directories are usually in the java distribution This is +# deduced from the value of JAVAC. When this macro completes, a list of +# directories is left in the variable JNI_INCLUDE_DIRS. +# +# Example usage follows: +# +# AX_JNI_INCLUDE_DIR +# +# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS +# do +# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR" +# done +# +# If you want to force a specific compiler: +# +# - at the configure.in level, set JAVAC=yourcompiler before calling +# AX_JNI_INCLUDE_DIR +# +# - at the configure level, setenv JAVAC +# +# Note: This macro can work with the autoconf M4 macros for Java programs. +# This particular macro is not part of the original set of macros. +# +# LICENSE +# +# Copyright (c) 2008 Don Anderson +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + + +# TSK: This has been modifed to not error out if JNI things cannot be resolved +# and to support scenarios whereby JAVAC is set to a location, but it is not +# on the path. + +#serial 7 + +AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR]) +AC_DEFUN([AX_JNI_INCLUDE_DIR],[ + +JNI_INCLUDE_DIRS="" + +test "x$JAVAC" = x && AC_MSG_ERROR(['\$JAVAC' undefined]) +AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no]) + +if test "x$_ACJNI_JAVAC" = xno; then + AS_ECHO(["$JAVAC could not be found in path -- cannot resolve to JNI headers"]) +else + _ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC") + _JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'` + case "$host_os" in + darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` + _JINC="$_JTOPDIR/Headers";; + *) _JINC="$_JTOPDIR/include";; + esac + _AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR]) + _AS_ECHO_LOG([_JINC=$_JINC]) + + # On Mac OS X 10.6.4, jni.h is a symlink: + # /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h + # -> ../../CurrentJDK/Headers/jni.h. + if test -f "$_JINC/jni.h" || test -L "$_JINC/jni.h"; then + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JINC" + else + _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` + if test -f "$_JTOPDIR/include/jni.h"; then + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include" + else + AS_ECHO(["cannot find java include files"]) + fi + fi + + if test "x$JNI_INCLUDE_DIRS" != x; then + # get the likely subdirectories for system specific java includes + case "$host_os" in + bsdi*) _JNI_INC_SUBDIRS="bsdos";; + linux*) _JNI_INC_SUBDIRS="linux genunix";; + osf*) _JNI_INC_SUBDIRS="alpha";; + solaris*) _JNI_INC_SUBDIRS="solaris";; + mingw*) _JNI_INC_SUBDIRS="win32";; + cygwin*) _JNI_INC_SUBDIRS="win32";; + *) _JNI_INC_SUBDIRS="genunix";; + esac + + # add any subdirectories that are present + for JINCSUBDIR in $_JNI_INC_SUBDIRS + do + if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR" + fi + done + fi +fi +]) + +# _ACJNI_FOLLOW_SYMLINKS +# Follows symbolic links on , +# finally setting variable _ACJNI_FOLLOWED +# ---------------------------------------- +AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[ +# find the include directory relative to the javac executable +_cur="$1" +while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do + AC_MSG_CHECKING([symlink for $_cur]) + _slink=`ls -ld "$_cur" | sed 's/.* -> //'` + case "$_slink" in + /*) _cur="$_slink";; + # 'X' avoids triggering unwanted echo options. + *) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";; + esac + AC_MSG_RESULT([$_cur]) +done +_ACJNI_FOLLOWED="$_cur" +])# _ACJNI diff --git a/configure.ac b/configure.ac index 75178858..8cc584f4 100644 --- a/configure.ac +++ b/configure.ac @@ -17,6 +17,7 @@ AC_PREFIX_DEFAULT([/usr/local/modsecurity]) AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) + LT_PREREQ([2.2]) LT_INIT([dlopen]) @@ -28,12 +29,16 @@ AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_GREP +AC_PROG_CXX 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 sys/utsname.h]) +AC_CHECK_HEADER([jni.h]) + # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST @@ -190,6 +195,46 @@ if test "$build_standalone_module" -eq 1; then fi +# Java Module +AC_ARG_ENABLE(java-module, + AS_HELP_STRING([--enable-java-module], + [Enable building java module.]), +[ + if test "$enableval" != "no"; then + build_java_module=1 + + m4_include([build/ax_jni_include_dir.m4]) + m4_include([build/ac_prog_javac_works.m4]) + m4_include([build/ac_prog_javac.m4]) + + # Test for java/jni so that we can compile the java bindings + AC_PROG_JAVAC + if test "x$JAVAC" != x; then + AX_JNI_INCLUDE_DIR + for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS + do + JNI_CPPFLAGS="$JNI_CPPFLAGS -I$JNI_INCLUDE_DIR" + done + # Export the paths so that the makefile gets them + AC_SUBST(JNI_CPPFLAGS, $JNI_CPPFLAGS) + + fi + AM_CONDITIONAL([X_JNI],[test "x$JNI_CPPFLAGS" != x]) + + AC_SUBST(STANDALONE_CPPFLAGS, "-I../standalone") + else + build_java_module=0 + fi +], +[ + build_java_module=0 +]) +AM_CONDITIONAL([BUILD_JAVA_MODULE], [test "$build_java_module" -eq 1]) +if test "$build_java_module" -eq 1; then + TOPLEVEL_SUBDIRS="$TOPLEVEL_SUBDIRS java" +fi + + # Extensions AC_ARG_ENABLE(extentions, AS_HELP_STRING([--enable-extentions], @@ -753,6 +798,9 @@ if test "$build_standalone_module" -ne 0; then AC_CONFIG_FILES([standalone/Makefile]) AC_CONFIG_FILES([nginx/modsecurity/config]) fi +if test "$build_java_module" -ne 0; then +AC_CONFIG_FILES([java/Makefile]) +fi if test "$build_extentions" -ne 0; then AC_CONFIG_FILES([ext/Makefile]) fi diff --git a/java/Makefile.am b/java/Makefile.am new file mode 100644 index 00000000..a9744099 --- /dev/null +++ b/java/Makefile.am @@ -0,0 +1,90 @@ +pkglibdir = $(prefix)/lib +pkglib_LTLIBRARIES = libModSecurityJNI.la + +libModSecurityJNI_la_SOURCES = ../apache2/mod_security2.c \ + ../apache2/apache2_config.c ../apache2/apache2_io.c ../apache2/apache2_util.c \ + ../apache2/re.c ../apache2/re_operators.c ../apache2/re_actions.c ../apache2/re_tfns.c \ + ../apache2/re_variables.c ../apache2/msc_logging.c ../apache2/msc_xml.c \ + ../apache2/msc_multipart.c ../apache2/modsecurity.c ../apache2/msc_parsers.c \ + ../apache2/msc_util.c ../apache2/msc_pcre.c ../apache2/persist_dbm.c ../apache2/msc_reqbody.c \ + ../apache2/msc_geo.c ../apache2/msc_gsb.c ../apache2/msc_unicode.c \ + ../apache2/acmp.c ../apache2/msc_lua.c ../apache2/msc_release.c \ + ../apache2/msc_crypt.c ../apache2/msc_tree.c ../apache2/libinjection/libinjection_sqli.c \ + ../standalone/api.c ../standalone/buckets.c \ + ../standalone/config.c ../standalone/filters.c \ + ../standalone/hooks.c ../standalone/regex.c ../standalone/server.c \ + org_modsecurity_ModSecurity.c + +libModSecurityJNI_la_CFLAGS = @APXS_CFLAGS@ @APR_CFLAGS@ @APU_CFLAGS@ \ + @PCRE_CFLAGS@ @LIBXML2_CFLAGS@ @LUA_CFLAGS@ @MODSEC_EXTRA_CFLAGS@ @CURL_CFLAGS@ +#libModSecurityJNI_la_CXXFLAGS = @APXS_CFLAGS@ @APR_CFLAGS@ @APU_CFLAGS@ \ +# @PCRE_CFLAGS@ @LIBXML2_CFLAGS@ @LUA_CFLAGS@ @MODSEC_EXTRA_CFLAGS@ @CURL_CFLAGS@ +libModSecurityJNI_la_CPPFLAGS = @APR_CPPFLAGS@ @PCRE_CPPFLAGS@ @LIBXML2_CPPFLAGS@ @JNI_CPPFLAGS@ +libModSecurityJNI_la_LIBADD = @APR_LDADD@ @APU_LDADD@ @PCRE_LDADD@ @LIBXML2_LDADD@ @LUA_LDADD@ + +if AIX +libModSecurityJNI_la_LDFLAGS = -module -avoid-version \ + @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ + @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ +endif + +if HPUX +libModSecurityJNI_la_LDFLAGS = -module -avoid-version \ + @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ + @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ +endif + +if MACOSX +libModSecurityJNI_la_LDFLAGS = -module -avoid-version \ + @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ + @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ +endif + +if SOLARIS +libModSecurityJNI_la_LDFLAGS = -module -avoid-version \ + @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ + @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ +endif + +if LINUX +libModSecurityJNI_la_LDFLAGS = -no-undefined -module -avoid-version -R @PCRE_LD_PATH@ \ + @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ + @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ +endif + +if FREEBSD +libModSecurityJNI_la_LDFLAGS = -no-undefined -module -avoid-version \ + @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ + @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ +endif + +if OPENBSD +libModSecurityJNI_la_LDFLAGS = -no-undefined -module -avoid-version \ + @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ + @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ +endif + +if NETBSD +libModSecurityJNI_la_LDFLAGS = -no-undefined -module -avoid-version \ + @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ + @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ +endif + + +if LINUX +install-exec-hook: $(pkglib_LTLIBRARIES) + @echo "Removing unused static libraries..."; \ + for m in $(pkglib_LTLIBRARIES); do \ + base=`echo $$m | sed 's/\..*//'`; \ + rm -f $(DESTDIR)$(pkglibdir)/$$base.*a; \ + install -D -m444 $(DESTDIR)$(pkglibdir)/$$base.so $(DESTDIR)$(APXS_MODULES)/$$base.so; \ + done +else +install-exec-hook: $(pkglib_LTLIBRARIES) + @echo "Removing unused static libraries..."; \ + for m in $(pkglib_LTLIBRARIES); do \ + base=`echo $$m | sed 's/\..*//'`; \ + rm -f $(DESTDIR)$(pkglibdir)/$$base.*a; \ + cp -p $(DESTDIR)$(pkglibdir)/$$base.so $(DESTDIR)$(APXS_MODULES); \ + done +endif diff --git a/java/ModSecurityJNI.vcxproj b/java/ModSecurityJNI.vcxproj index c0cc95b5..d25bd911 100644 --- a/java/ModSecurityJNI.vcxproj +++ b/java/ModSecurityJNI.vcxproj @@ -164,7 +164,7 @@ - + diff --git a/java/ModSecurityJNI.vcxproj.filters b/java/ModSecurityJNI.vcxproj.filters index f9f2b7eb..2a6c2c72 100644 --- a/java/ModSecurityJNI.vcxproj.filters +++ b/java/ModSecurityJNI.vcxproj.filters @@ -117,7 +117,7 @@ Standalone Sources - + diff --git a/java/ModSecurityLoader/dist/ModSecurityLoader.jar b/java/ModSecurityLoader/dist/ModSecurityLoader.jar index f1c1180b..760dcf1b 100644 Binary files a/java/ModSecurityLoader/dist/ModSecurityLoader.jar and b/java/ModSecurityLoader/dist/ModSecurityLoader.jar differ diff --git a/java/ModSecurityLoader/dist/README.TXT b/java/ModSecurityLoader/dist/README.TXT new file mode 100644 index 00000000..dadc15cf --- /dev/null +++ b/java/ModSecurityLoader/dist/README.TXT @@ -0,0 +1,32 @@ +======================== +BUILD OUTPUT DESCRIPTION +======================== + +When you build an Java application project that has a main class, the IDE +automatically copies all of the JAR +files on the projects classpath to your projects dist/lib folder. The IDE +also adds each of the JAR files to the Class-Path element in the application +JAR files manifest file (MANIFEST.MF). + +To run the project from the command line, go to the dist folder and +type the following: + +java -jar "ModSecurityLoader.jar" + +To distribute this project, zip up the dist folder (including the lib folder) +and distribute the ZIP file. + +Notes: + +* If two JAR files on the project classpath have the same name, only the first +JAR file is copied to the lib folder. +* Only JAR files are copied to the lib folder. +If the classpath contains other types of files or folders, these files (folders) +are not copied. +* If a library on the projects classpath also has a Class-Path element +specified in the manifest,the content of the Class-Path element has to be on +the projects runtime path. +* To set a main class in a standard Java project, right-click the project node +in the Projects window and choose Properties. Then click Run and enter the +class name in the Main Class field. Alternatively, you can manually type the +class name in the manifest Main-Class element. diff --git a/java/ModSecurityLoader/src/org/modsecurity/loader/ModSecurityLoader.java b/java/ModSecurityLoader/src/org/modsecurity/loader/ModSecurityLoader.java index 5008a573..12465a6d 100644 --- a/java/ModSecurityLoader/src/org/modsecurity/loader/ModSecurityLoader.java +++ b/java/ModSecurityLoader/src/org/modsecurity/loader/ModSecurityLoader.java @@ -4,30 +4,37 @@ import java.io.File; public class ModSecurityLoader { - private static final String MODSECURITYLIBSDIR_PATH = "c:\\work\\mod_security\\java\\libs\\"; //directory with ModSecurity native libraries + //private static final String MODSECURITYLIBSDIR_PATH = "c:\\work\\mod_security\\java\\libs\\"; //directory with ModSecurity native libraries static { - File modSecDir = new File(MODSECURITYLIBSDIR_PATH); - - File[] flibs = modSecDir.listFiles(); - - loadLib(flibs, "zlib1"); - loadLib(flibs, "libxml2"); - loadLib(flibs, "pcre"); - loadLib(flibs, "libapr-1"); - loadLib(flibs, "libapriconv-1"); - loadLib(flibs, "libaprutil-1"); - loadLib(flibs, "ModSecurityJNI"); + System.out.println("ModSecurity loader static block executed."); +// File modSecDir = new File(MODSECURITYLIBSDIR_PATH); +// File[] flibs = modSecDir.listFiles(); +// loadLib(flibs, "zlib1"); +// loadLib(flibs, "libxml2"); +// loadLib(flibs, "pcre"); +// loadLib(flibs, "libapr-1"); +// loadLib(flibs, "libapriconv-1"); +// loadLib(flibs, "libaprutil-1"); +// loadLib(flibs, "ModSecurityJNI"); //alternative load, this requires native libraries to be in java.library.path, you can set it //by specifying server VM start-up option: -Djava.library.path=path/to/libs/ -// System.loadLibrary("zlib1"); -// System.loadLibrary("libxml2"); -// System.loadLibrary("pcre"); -// System.loadLibrary("libapr-1"); -// System.loadLibrary("libapriconv-1"); -// System.loadLibrary("libaprutil-1"); -// System.loadLibrary("ModSecurityJNI"); + try { + System.loadLibrary("zlib1"); //needed for libxml2 in Windows + } catch(UnsatisfiedLinkError ex) { + } + System.loadLibrary("libxml2"); + System.loadLibrary("pcre"); + System.loadLibrary("libapr-1"); + try { + System.loadLibrary("libapriconv-1"); + } catch(UnsatisfiedLinkError ex) { //needed for libaprutil-1 in Windows + } + System.loadLibrary("libaprutil-1"); + System.loadLibrary("ModSecurityJNI"); + + System.out.println("ModSecurity native libraries loaded."); } private static void loadLib(File[] files, String lib) { diff --git a/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurity.java b/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurity.java index 30f35821..506bf0c4 100644 --- a/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurity.java +++ b/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurity.java @@ -7,10 +7,7 @@ import java.net.UnknownHostException; import javax.servlet.FilterConfig; import javax.servlet.ServletException; -/** - * - * @author Mihai Pitu - */ + public final class ModSecurity { //From build/classes: >"c:\Program Files\Java\jdk1.7.0_05\bin\javah.exe" -classpath c:\work\apache-tomcat-7.0.39\lib\servlet-api.jar;. org.modsecurity.ModSecurity @@ -23,14 +20,14 @@ public final class ModSecurity { static { //ModSecurityLoader calls System.load() for every native library needed by ModSecurity. - try { - Class.forName("org.modsecurity.loader.ModSecurityLoader"); - System.out.println("ModSecurity libraries loaded."); - } catch (ClassNotFoundException ex) { - java.util.logging.Logger.getLogger(ModSecurity.class.getName()).log(java.util.logging.Level.SEVERE, - "ModSecurityLoader was not found, please make sure that you have \"ModSecurityLoader.jar\" in your server lib folder.", ex); - } - +// try { +// Class.forName("org.modsecurity.loader.ModSecurityLoader"); +// System.out.println("ModSecurity libraries loaded."); +// } catch (ClassNotFoundException ex) { +// java.util.logging.Logger.getLogger(ModSecurity.class.getName()).log(java.util.logging.Level.SEVERE, +// "ModSecurityLoader was not found, please make sure that you have \"ModSecurityLoader.jar\" in your server lib folder.", ex); +// } + //If the ModSecurityLoader is not used, native libraries can be loaded here, however this is bad practice since this will raise UnsatisfiedLinkError if //ModSecurity is used in multiple webapps. This will also will raise problems when the web-app is redeployed and the server is running. // System.load("c:\\work\\mod_security\\java\\libs\\zlib1.dll"); @@ -40,6 +37,19 @@ public final class ModSecurity { // System.load("c:\\work\\mod_security\\java\\libs\\libapriconv-1.dll"); // System.load("c:\\work\\mod_security\\java\\libs\\libaprutil-1.dll"); // System.load("c:\\work\\mod_security\\java\\Debug\\ModSecurityJNI.dll"); + try { + System.loadLibrary("zlib1"); //needed for libxml2 in Windows + } catch(UnsatisfiedLinkError ex) { + } + System.loadLibrary("libxml2"); + System.loadLibrary("pcre"); + System.loadLibrary("libapr-1"); + try { + System.loadLibrary("libapriconv-1"); + } catch(UnsatisfiedLinkError ex) { //needed for libaprutil-1 in Windows + } + System.loadLibrary("libaprutil-1"); + System.loadLibrary("ModSecurityJNI"); } public ModSecurity(FilterConfig fc, String confFile) throws ServletException { diff --git a/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurityFilter.java b/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurityFilter.java index 0892a049..4bd836a4 100644 --- a/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurityFilter.java +++ b/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurityFilter.java @@ -1,7 +1,6 @@ package org.modsecurity; import java.io.IOException; -import java.net.URLDecoder; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; diff --git a/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletRequest.java b/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletRequest.java index 2982298d..3b543eea 100644 --- a/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletRequest.java +++ b/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletRequest.java @@ -491,7 +491,7 @@ public class MsHttpServletRequest extends HttpServletRequestWrapper { */ @Override public Enumeration getParameterNames() { - Hashtable parameterNames = new Hashtable(); + Hashtable parameterNames = new Hashtable(); for (int i = 0, n = parameters.size(); i < n; i++) { Parameter p = (Parameter) parameters.get(i); parameterNames.put(p.name, p.value); diff --git a/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletResponse.java b/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletResponse.java index 0264f8f2..c0f0d314 100644 --- a/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletResponse.java +++ b/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletResponse.java @@ -25,8 +25,8 @@ public class MsHttpServletResponse extends HttpServletResponseWrapper { private static final int INTERCEPT_OBSERVE_ONLY = 2; public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1"; private int interceptMode = INTERCEPT_ON; - private ArrayList headers = new ArrayList(); - private ArrayList cookies = new ArrayList(); + private ArrayList headers = new ArrayList(); + private ArrayList cookies = new ArrayList(); private int status = -1; private boolean committed = false; private boolean suspended = false; @@ -593,11 +593,11 @@ final class FastHttpDateFormat { /** * Formatter cache. */ - protected static final HashMap formatCache = new HashMap(); + protected static final HashMap formatCache = new HashMap(); /** * Parser cache. */ - protected static final HashMap parseCache = new HashMap(); + protected static final HashMap parseCache = new HashMap(); // --------------------------------------------------------- Public Methods /** @@ -705,7 +705,7 @@ final class FastHttpDateFormat { /** * Update cache. */ - private static void updateCache(HashMap cache, Object key, + private static void updateCache(HashMap cache, Object key, Object value) { if (value == null) { return; diff --git a/java/ModSecurityTestApp/web/help.html b/java/ModSecurityTestApp/web/help.html index efb5d153..6560f9e8 100644 --- a/java/ModSecurityTestApp/web/help.html +++ b/java/ModSecurityTestApp/web/help.html @@ -13,7 +13,7 @@ } - +
@@ -39,17 +39,16 @@


-

Installation

+

Installation

First you need to choose whether to download and compile ModSecurity from the project's version control web-site: github.com/SpiderLabs/ModSecurity or using pre-compiled binaries from - modsecurity.org. We will not discuss how to compile - the dependent native libraries needed since these steps are described in the README files from ModSecurity's repository. + modsecurity.org. The native libraries (.so, .dll, etc.) needed for ModSecurity for Java are:

  1. - zlib1 + zlib1 (Windows only)
  2. libxml2 @@ -61,25 +60,53 @@ libapr-1
  3. - libapriconv-1 + libapriconv-1 (Windows only)
  4. libaprutil-1
  5. - ModSecurityJNI + ModSecurityJNI (JNI wrapper for mod_security code)

- These native libraries are loaded by the ModSecurityLoader.jar, which should be placed in your Java server library loader - (for example, in Tomcat 7: $CATALINA_HOME/lib). You can build or modify the load directory of ModSecurityLoader from - /mod_security/java/ModSecurityLoader/src/. The libraries have to be copied in a directory (for example, c:\work\mod_security\java\libs\), - which should be accessible to ModSecurityLoader.jar. + These native libraries are used by the ModSecurityFilter.


-

Java Web Applications with ModSecurity Filter

+

Compile ModSecurity native library

+

+ Install required packages for compilation. For example, on Debian/Ubuntu like systems (Windows users have a Visual Studio solution): +

+
+sudo apt-get install g++ make automake autoconf libtool
+                            
+

+ Install required dependent packages: +

+
+sudo apt-get install libxml2 libxml2-dev libxml2-utils libaprutil1 libaprutil1-dev apache2-prefork-dev
+                            
+

+ Download mod_security source code from GitHub, compile and install: +

+
+cd mod_security/
+./autogen.sh
+./configure --enable-java-module
+make
+                            
+

+ Copy compiled library in a convenient folder: +

+
+sudo cp ./java/.libs/libModSecurityJNI.so /usr/lib/
+                            
+ + +
+

Java Web Applications with ModSecurity Filter

ModSecurity for Java uses Java Filters in order to intercept Http requests and responses. ModsecurityTestApp is an example of Java EE Web application using the ModSecurity @@ -110,15 +137,41 @@ </filter> - +

The ModSecurity Filter makes use of the native libraries written in C/C++ using the JNI technology. - As stated earlier, the native libraries are loaded by the ModSecurityLoader.jar - which should be loaded by the server at start-up. If you are unable to configure the server to load the - ModSecurity libraries at startup, you may load them in your web application although this is not - recommended because this will raise UnsatisfiedLinkError if the ModSecurity - Filter is used in multiple applications within the same server. -

+ There are two ways of loading native libraries by Java Web Applications: +
    +
  1. +

    Loading native libraries directly in the ModSecurityFilter

    +

    + Although this is the easier, this is not recommended because the JVM will raise + UnsatisfiedLinkError if the ModSecurity Filter is used in + multiple applications within the same server. + The libraries are loaded in the ModSecurity class using + System.loadLibrary(). In this case the server has to be started with + the following VM options: +

    +
    +-Djava.library.path=/path/to/libraries/folder/
    +                                    
    +

    + You can specify multiple folders for the java.library.path variable by using + : (colon) or ; (semi-colon), depending on your environment. +

    +
  2. + +
  3. +

    Loading native libraries when the Web Server starts

    +

    + ModSecurityLoader.jar should be placed + in the Java server library loader folder (for example, in Tomcat 7: $CATALINA_HOME/lib). + You can build or modify the load directory of ModSecurityLoader from + /mod_security/java/ModSecurityLoader/src/. +

    +
  4. +
+


diff --git a/java/org_modsecurity_ModSecurity.c b/java/org_modsecurity_ModSecurity.c new file mode 100644 index 00000000..64d8865a --- /dev/null +++ b/java/org_modsecurity_ModSecurity.c @@ -0,0 +1,797 @@ +#undef inline +#define inline __inline + +#include "org_modsecurity_ModSecurity.h" +#include "api.h" + +#include + +#define MODSECURITY_JAVACLASS "org/modsecurity/ModSecurity" +#define SERVLETINPUTSTREAM_JAVACLASS "javax/servlet/ServletInputStream" + +#define TOSTRING_MET "toString" +#define STRINGRETURN_SIG "()Ljava/lang/String;" + +#define INPUTSTREAM_READ_MET "read" +#define INPUTSTREAM_READ_SIG "([BII)I" + +#define MODSECURITY_LOG_MET "log" +#define MODSECURITY_LOG_SIG "(ILjava/lang/String;)V" + +#define MODSECURITY__HTTPREQHEADERS_MET "getHttpRequestHeaders" +#define MODSECURITY__HTTPREQHEADERS_SIG "(Ljavax/servlet/http/HttpServletRequest;)[[Ljava/lang/String;" +#define MODSECURITY__HTTPRESHEADERS_MET "getHttpResponseHeaders" +#define MODSECURITY__HTTPRESHEADERS_SIG "(Ljavax/servlet/http/HttpServletResponse;)[[Ljava/lang/String;" +#define MODSECURITY__ISPV6_MET "isIPv6" +#define MODSECURITY__ISPV6_SIG "(Ljava/lang/String;)Z" + +#define HTTPTRANSACTION_HTTPREQUEST_MET "getHttpRequest" +#define HTTPTRANSACTION_HTTPREQUEST_SIG "()Ljavax/servlet/http/HttpServletRequest;" +#define HTTPTRANSACTION_MSHTTPREQUEST_MET "getMsHttpRequest" +#define HTTPTRANSACTION_MSHTTPREQUEST_SIG "()Lorg/modsecurity/MsHttpServletRequest;" +#define HTTPTRANSACTION_MSHTTPRESPONSE_MET "getMsHttpResponse" +#define HTTPTRANSACTION_MSHTTPRESPONSE_SIG "()Lorg/modsecurity/MsHttpServletResponse;" + +#define HTTPTRANSACTION_TRANSACTIONID_MET "getTransactionID" + +#define SERVLETREQUEST_SERVERNAME_MET "getServerName" +#define SERVLETREQUEST_CHARENCODING_MET "getCharacterEncoding" +#define SERVLETREQUEST_CONTENTTYPE_MET "getContentType" +#define SERVLETREQUEST_SERVERPORT_MET "getServerPort" +#define SERVLETREQUEST_SERVERPORT_SIG "()I" +#define SERVLETREQUEST_REMOTEADDR_MET "getRemoteAddr" + +#define SERVLETREQUEST_INPUTSTREAM_MET "getInputStream" +#define SERVLETREQUEST_INPUTSTREAM_SIG "()Ljavax/servlet/ServletInputStream;" + +#define HTTPSERVLETREQUEST_PATHINFO_MET "getPathInfo" +#define HTTPSERVLETREQUEST_QUERYSTRING_MET "getQueryString" +#define HTTPSERVLETREQUEST_METHOD_MET "getMethod" +#define HTTPSERVLETREQUEST_PROTOCOL_MET "getProtocol" + +#define HTTPSERVLETREQUEST_REQUESTURL_MET "getRequestURL" +#define HTTPSERVLETREQUEST_REQUESTURL_SIG "()Ljava/lang/StringBuffer;" + +#define MSHTTPSERVLETREQUEST_READBODY_MET "readBody" +#define MSHTTPSERVLETREQUEST_READBODY_SIG "(I)V" +#define MSHTTPSERVLETREQUEST_SETBODY_MET "setBodyBytes" +#define MSHTTPSERVLETREQUEST_SETBODY_SIG "([B)V" + +#define SERVLETRESPONSE_CONTENTTYPE_MET "getContentType" +#define SERVLETRESPONSE_CHARENCODING_MET "getCharacterEncoding" + +#define MSSERVLETRESPONSE_OUTPUTSTREAM_MET "getByteArrayStream" +#define MSSERVLETRESPONSE_OUTPUTSTREAM_SIG "()Ljava/io/ByteArrayInputStream;" + + +//typedef struct { +JavaVM *jvm; +jobject modSecurityInstance; +directory_config *config; +//} JavaModSecurityContext; +jmethodID logMethod; + + +apr_table_t *requests; +apr_pool_t *requestsPool; +char *serverHostname; + +#define JAVASERVLET_INSTREAM "MSReqBStr" +#define JAVASERVLET_OUTSTREAM "MSResBStr" +#define JAVASERVLET_TRANSACTION "MSTran" + +void storeJavaServletContext(request_rec *r, const char *key, jobject obj) +{ + apr_table_setn(r->notes, key, (const char *)obj); +} + +void removeJavaServletContext(request_rec *r, const char *key) +{ + apr_table_unset(r->notes, key); +} + +jobject getJavaServletContext(request_rec *r, const char *key) +{ + jobject obj = NULL; + request_rec *rx = NULL; + + /* Look in the current request first. */ + obj = (jobject)apr_table_get(r->notes, key); + if (obj != NULL) + return obj; + + /* If this is a subrequest then look in the main request. */ + if (r->main != NULL) + { + obj = (jobject)apr_table_get(r->main->notes, key); + if (obj != NULL) + { + return obj; + } + } + + /* If the request was redirected then look in the previous requests. */ + rx = r->prev; + while(rx != NULL) + { + obj = (jobject)apr_table_get(rx->notes, key); + if (obj != NULL) + { + return obj; + } + rx = rx->prev; + } + + return NULL; +} + + +apr_sockaddr_t *CopySockAddr(jclass msClass, JNIEnv *env, apr_pool_t *pool, char *addrstr, jstring addrStrJstr) +{ + jmethodID isIPv6Met = (*env)->GetStaticMethodID(env, msClass, MODSECURITY__ISPV6_MET, MODSECURITY__ISPV6_SIG); + jboolean isIPv6 = (*env)->CallStaticBooleanMethod(env, msClass, isIPv6Met, addrStrJstr); + + apr_sockaddr_t *addr = (apr_sockaddr_t *)apr_palloc(pool, sizeof(apr_sockaddr_t)); + int adrlen = 16, iplen = 4; + + if(isIPv6) + { + adrlen = 46; + iplen = 16; + addr->family = AF_INET6; + } + else + addr->family = AF_INET; + + addr->addr_str_len = adrlen; + + + addr->hostname = (char*) "unknown"; +#ifdef WIN32 + addr->ipaddr_len = sizeof(IN_ADDR); +#else + addr->ipaddr_len = sizeof(struct in_addr); +#endif + addr->ipaddr_ptr = &addr->sa.sin.sin_addr; + addr->pool = pool; + addr->port = 80; +#ifdef WIN32 + memcpy(&addr->sa.sin.sin_addr.S_un.S_addr, addrstr, iplen); +#else + memcpy(&addr->sa.sin.sin_addr.s_addr, addrstr, iplen); +#endif + addr->sa.sin.sin_family = addr->family; + addr->sa.sin.sin_port = 80; + addr->salen = sizeof(addr->sa); + addr->servname = addr->hostname; + + return addr; +} + +inline char* fromJString(JNIEnv *env, jstring jStr, apr_pool_t *pool) +{ + char *str; + if (jStr != NULL) + { + const char *jCStr = (*env)->GetStringUTFChars(env, jStr, NULL); + int len = strlen(jCStr); + str = (char*) apr_palloc(pool, len + 1); + memcpy(str, jCStr, len); + str[len] = '\0'; //null terminate + (*env)->ReleaseStringUTFChars(env, jStr, jCStr); //release java memory + } + else + str = (char*) ""; + + return str; +} + +inline char* fromJStringMethod(JNIEnv *env, jmethodID method, jobject obj, apr_pool_t *pool) +{ + jstring jStr = (jstring) (*env)->CallObjectMethod(env, obj, method); + + return fromJString(env, jStr, pool); +} + + +void logSec(void *obj, int level, char *str) +{ + JNIEnv *env; + jstring jStr; + + if (!(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL)) //get the Enviroment from the JavaVM + { + jStr = (*env)->NewStringUTF(env, str); + + (*env)->CallVoidMethod(env, modSecurityInstance, logMethod, level, jStr); + + (*jvm)->DetachCurrentThread(jvm); + //in the context of a JVM thread, any leaked local references are automatically cleaned up + //(*env)->ReleaseStringUTFChars(jStr, str); + } +} + + +apr_status_t ReadBodyCallback(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos) +{ + jobject inputStream = getJavaServletContext(r, JAVASERVLET_INSTREAM); //servlet request input stream + JNIEnv *env; + + *readcnt = 0; + + if(inputStream == NULL) + { + *is_eos = 1; + return APR_SUCCESS; + } + + if (!(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL)) + { + //read request body from the servlet input stream using 'read' method + jclass inputStreamClass = (*env)->FindClass(env, SERVLETINPUTSTREAM_JAVACLASS); + jmethodID read = (*env)->GetMethodID(env, inputStreamClass, INPUTSTREAM_READ_MET, INPUTSTREAM_READ_SIG); + + jbyteArray byteArrayBuf = (*env)->NewByteArray(env, length); + + jint count = (*env)->CallIntMethod(env, inputStream, read, byteArrayBuf, 0, length); + jbyte* bufferPtr = (*env)->GetByteArrayElements(env, byteArrayBuf, NULL); + + if (count == -1 || count > length || (*env)->ExceptionCheck(env) == JNI_TRUE) //end of stream + { + *is_eos = 1; + } + else + { + *readcnt = count; + + memcpy(buf, bufferPtr, *readcnt); + } + (*env)->ReleaseByteArrayElements(env, byteArrayBuf, bufferPtr, 0); + (*env)->DeleteLocalRef(env, byteArrayBuf); + + (*jvm)->DetachCurrentThread(jvm); + } + + return APR_SUCCESS; +} + +apr_status_t WriteBodyCallback(request_rec *r, char *buf, unsigned int length) +{ + jobject httpTransaction; + JNIEnv *env; + jclass httpTransactionClass; + jmethodID getHttpRequest; + jobject httpServletRequest; + jclass httpServletRequestClass; + jmethodID setBodyBytes; + jbyte *jbuf; + int i; + jbyteArray byteArrayBuf; + + + httpTransaction = getJavaServletContext(r, JAVASERVLET_TRANSACTION); + + if(httpTransaction == NULL) + { + return APR_SUCCESS; + } + + if (!(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL)) + { + httpTransactionClass = (*env)->GetObjectClass(env, httpTransaction); + + getHttpRequest = (*env)->GetMethodID(env, httpTransactionClass, HTTPTRANSACTION_MSHTTPREQUEST_MET, HTTPTRANSACTION_MSHTTPREQUEST_SIG); + httpServletRequest = (*env)->CallObjectMethod(env, httpTransaction, getHttpRequest); + + httpServletRequestClass = (*env)->GetObjectClass(env, httpServletRequest); + setBodyBytes = (*env)->GetMethodID(env, httpServletRequestClass, MSHTTPSERVLETREQUEST_SETBODY_MET, MSHTTPSERVLETREQUEST_SETBODY_SIG); + + jbuf = (jbyte*) apr_palloc(requestsPool, sizeof(jbyte) * length); + for (i = 0; i < length; i++) + jbuf[i] = buf[i]; + + byteArrayBuf = (*env)->NewByteArray(env, length); + (*env)->SetByteArrayRegion(env, byteArrayBuf, 0, length, jbuf); + + //on setBodyBytes we copy buf bytes + (*env)->CallVoidMethod(env, httpServletRequest, setBodyBytes, byteArrayBuf); + + //(*env)->ReleaseByteArrayElements(byteArrayBuf, jbuf, NULL); + + (*jvm)->DetachCurrentThread(jvm); + } + return APR_SUCCESS; +} + +apr_status_t ReadResponseCallback(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos) +{ + jobject inputStream; + JNIEnv *env; + jclass inputStreamClass; + jmethodID read; + jbyteArray byteArrayBuf; + jint count; + jbyte* bufferPtr; + + + inputStream = getJavaServletContext(r, JAVASERVLET_OUTSTREAM); + *readcnt = 0; + + if(inputStream == NULL) + { + *is_eos = 1; + return APR_SUCCESS; + } + + if (!(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL)) + { + inputStreamClass = (*env)->GetObjectClass(env, inputStream); + read = (*env)->GetMethodID(env, inputStreamClass, INPUTSTREAM_READ_MET, INPUTSTREAM_READ_SIG); + + byteArrayBuf = (*env)->NewByteArray(env, length); + + count = (*env)->CallIntMethod(env, inputStream, read, byteArrayBuf, 0, length); + bufferPtr = (*env)->GetByteArrayElements(env, byteArrayBuf, NULL); + + if (count == -1 || count > length || (*env)->ExceptionCheck(env) == JNI_TRUE) //end of stream + { + *is_eos = 1; + } + else + { + *readcnt = count; + + memcpy(buf, bufferPtr, *readcnt); + } + (*env)->ReleaseByteArrayElements(env, byteArrayBuf, bufferPtr, 0); + (*env)->DeleteLocalRef(env, byteArrayBuf); + + (*jvm)->DetachCurrentThread(jvm); + } + + return APR_SUCCESS; +} + +apr_status_t WriteResponseCallback(request_rec *r, char *buf, unsigned int length) +{ + JNIEnv *env; + jbyteArray byteArrayBuf; + jclass httpTransactionClass; + jmethodID getHttpResponse; + jobject httpServletResponse; + jbyte *jbuf; + int i; + jmethodID setBodyBytes; + jclass httpServletResponseClass; + jobject httpTransaction = getJavaServletContext(r, JAVASERVLET_TRANSACTION); + + if(httpTransaction == NULL) + { + (*jvm)->DetachCurrentThread(jvm); + return APR_SUCCESS; + } + + if (!(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL)) + { + httpTransactionClass = (*env)->GetObjectClass(env, httpTransaction); + + getHttpResponse = (*env)->GetMethodID(env, httpTransactionClass, HTTPTRANSACTION_MSHTTPRESPONSE_MET, HTTPTRANSACTION_MSHTTPRESPONSE_SIG); + httpServletResponse = (*env)->CallObjectMethod(env, httpTransaction, getHttpResponse); + + + httpServletResponseClass = (*env)->GetObjectClass(env, httpServletResponse); + setBodyBytes = (*env)->GetMethodID(env, httpServletResponseClass, MSHTTPSERVLETREQUEST_SETBODY_MET, MSHTTPSERVLETREQUEST_SETBODY_SIG); + + jbuf = (jbyte*) apr_palloc(requestsPool, sizeof(jbyte) * length); + for (i = 0; i < length; i++) + jbuf[i] = buf[i]; + + byteArrayBuf = (*env)->NewByteArray(env, length); + (*env)->SetByteArrayRegion(env, byteArrayBuf, 0, length, jbuf); + + //on setBodyBytes we copy buf bytes + (*env)->CallVoidMethod(env, httpServletResponse, setBodyBytes, byteArrayBuf); + + //(*env)->ReleaseByteArrayElements(byteArrayBuf, jbuf, NULL); + + (*jvm)->DetachCurrentThread(jvm); + } + return APR_SUCCESS; +} + +JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_initialize(JNIEnv *env, jobject obj, jstring serverName) +{ + (*env)->GetJavaVM(env, &jvm); + modSecurityInstance = (*env)->NewGlobalRef(env, obj); //Save the ModSecurity object for further use + + logMethod = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, obj), MODSECURITY_LOG_MET, MODSECURITY_LOG_SIG); //log method ID + + modsecSetLogHook(NULL, logSec); + + modsecSetReadBody(ReadBodyCallback); + modsecSetReadResponse(ReadResponseCallback); + modsecSetWriteBody(WriteBodyCallback); + modsecSetWriteResponse(WriteResponseCallback); + + modsecInit(); + + modsecStartConfig(); + config = modsecGetDefaultConfig(); + modsecFinalizeConfig(); + modsecInitProcess(); + config = NULL; + + //table for requests + apr_pool_create(&requestsPool, NULL); + requests = apr_table_make(requestsPool, 10); + + serverHostname = fromJString(env, serverName, requestsPool); + + return APR_SUCCESS; +} + +JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_destroy(JNIEnv *env, jobject obj) +{ + (*env)->DeleteGlobalRef(env, modSecurityInstance); + + apr_pool_destroy(requestsPool); + + modsecTerminate(); + + return APR_SUCCESS; +} + + +inline void setHeaders(JNIEnv *env, jclass httpServletRequestClass, jobject httpServletR, apr_table_t *reqHeaders, apr_pool_t *pool, const char *headersMet, const char *headersSig) +{ + jmethodID getHttpHeaders; + jobjectArray headersTable; + jsize size; + int i; + + //All headers are returned in a table by a static method from ModSecurity class + getHttpHeaders = (*env)->GetStaticMethodID(env, httpServletRequestClass, headersMet, headersSig); + headersTable = (jobjectArray) (*env)->CallStaticObjectMethod(env, httpServletRequestClass, getHttpHeaders, httpServletR); + size = (*env)->GetArrayLength(env, headersTable); + + for (i = 0; i < size; i++) + { + const char *headerName; + const char *headerValue; + jobjectArray row = (jobjectArray) (*env)->GetObjectArrayElement(env, headersTable, i); + jstring headerNameJStr = (jstring) (*env)->GetObjectArrayElement(env, row, 0); + jstring headerValueJStr = (jstring) (*env)->GetObjectArrayElement(env, row, 1); + + if (headerNameJStr != NULL && headerValueJStr != NULL) + { + headerName = fromJString(env, headerNameJStr, pool); + //apr_pool_cleanup_register(r->pool, headerName, memCleanup, apr_pool_cleanup_null); + + headerValue = fromJString(env, headerValueJStr, pool); + //apr_pool_cleanup_register(r->pool, headerValue, memCleanup, apr_pool_cleanup_null); + + apr_table_setn(reqHeaders, headerName, headerValue); + + (*env)->DeleteLocalRef(env, headerNameJStr); + (*env)->DeleteLocalRef(env, headerValueJStr); + } + } +} + +JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, jobject obj, jstring configPath, jobject httpTransaction, jboolean reloadConfig) +{ + conn_rec *c; + request_rec *r; + const char *err; + jmethodID getHttpRequest; + jclass httpTransactionClass; + jobject httpServletRequest; + jobject servletRequest; + jclass httpServletRequestClass; + jclass servletRequestClass; + jclass modSecurityClass; + jmethodID readBody; + jmethodID getTransactionID; + jmethodID getInputStream; + const char *reqID; + jobject inputStream; + jmethodID getServerName; + jmethodID getServerPort; + int port; + char *port_str; + jmethodID getPathInfo; + jmethodID getQueryString; + jmethodID getCharacterEncoding; + jmethodID getContentType; + const char *lng; + jmethodID getMethod; + const char* method; + jmethodID getProtocol; + jmethodID getRequestURL; + jobject stringBuffer; + jmethodID toStringBuff; + char *url; + jmethodID getRemoteAddr; + jstring remoteAddrJStr; + char *remoteAddr; + int status; + int len; + + if (config == NULL || reloadConfig) + { + const char *path; + config = modsecGetDefaultConfig(); + path = fromJString(env, configPath, config->mp); //path to modsecurity.conf + + err = modsecProcessConfig(config, path, NULL); + + if(err != NULL) + { + logSec(NULL, 0, (char*)err); + + return DONE; + } + } + + c = modsecNewConnection(); + modsecProcessConnection(c); + r = modsecNewRequest(c, config); + + r->server->server_hostname = serverHostname; + httpTransactionClass = (*env)->GetObjectClass(env, httpTransaction); + getHttpRequest = (*env)->GetMethodID(env, httpTransactionClass, HTTPTRANSACTION_MSHTTPREQUEST_MET, HTTPTRANSACTION_MSHTTPREQUEST_SIG); + + httpServletRequest = (*env)->CallObjectMethod(env, httpTransaction, getHttpRequest); + servletRequest = httpServletRequest; //superclass of HttpServletRequest + + httpServletRequestClass = (*env)->GetObjectClass(env, httpServletRequest); //MsHttpServletRequest interface + servletRequestClass = (*env)->GetObjectClass(env, servletRequest); //ServletRequest interface + modSecurityClass = (*env)->GetObjectClass(env, obj); //ModSecurity class + + //readBody method reads all bytes from the inputStream or a maximum of 'config->reqbody_limit' bytes + readBody = (*env)->GetMethodID(env, httpServletRequestClass, MSHTTPSERVLETREQUEST_READBODY_MET, MSHTTPSERVLETREQUEST_READBODY_SIG); + (*env)->CallVoidMethod(env, httpServletRequest, readBody, config->reqbody_limit); + + if ((*env)->ExceptionCheck(env) == JNI_TRUE) //read body raised an Exception, return to JVM + { + modsecFinishRequest(r); + return DONE; + } + + + getTransactionID = (*env)->GetMethodID(env, httpTransactionClass, HTTPTRANSACTION_TRANSACTIONID_MET, STRINGRETURN_SIG); + reqID = fromJStringMethod(env, getTransactionID, httpTransaction, r->pool); //fromJString(env, requestID, r->pool); //unique ID of this request + apr_table_setn(requests, reqID, (const char*) r); //store this request for response processing + + + getInputStream = (*env)->GetMethodID(env, httpServletRequestClass, SERVLETREQUEST_INPUTSTREAM_MET, SERVLETREQUEST_INPUTSTREAM_SIG); + inputStream = (*env)->CallObjectMethod(env, httpServletRequest, getInputStream); //Request body input stream used in the read body callback + + storeJavaServletContext(r, JAVASERVLET_INSTREAM, inputStream); + storeJavaServletContext(r, JAVASERVLET_TRANSACTION, httpTransaction); + + + getServerName = (*env)->GetMethodID(env, servletRequestClass, SERVLETREQUEST_SERVERNAME_MET, STRINGRETURN_SIG); + r->hostname = fromJStringMethod(env, getServerName, servletRequest, r->pool); + + getServerPort = (*env)->GetMethodID(env, servletRequestClass, SERVLETREQUEST_SERVERPORT_MET, SERVLETREQUEST_SERVERPORT_SIG); + port = (*env)->CallIntMethod(env, servletRequest, getServerPort); //server port + port_str = apr_itoa(r->pool, port); + + + getPathInfo = (*env)->GetMethodID(env, httpServletRequestClass, HTTPSERVLETREQUEST_PATHINFO_MET, STRINGRETURN_SIG); + r->path_info = fromJStringMethod(env, getPathInfo, httpServletRequest, r->pool); + + + getQueryString = (*env)->GetMethodID(env, httpServletRequestClass, HTTPSERVLETREQUEST_QUERYSTRING_MET, STRINGRETURN_SIG); + r->args = fromJStringMethod(env, getQueryString, httpServletRequest, r->pool); + + + setHeaders(env, httpServletRequestClass, httpServletRequest, r->headers_in, r->pool, MODSECURITY__HTTPREQHEADERS_MET, MODSECURITY__HTTPREQHEADERS_SIG); + + + getCharacterEncoding = (*env)->GetMethodID(env, servletRequestClass, SERVLETREQUEST_CHARENCODING_MET, STRINGRETURN_SIG); + r->content_encoding = fromJStringMethod(env, getCharacterEncoding, servletRequest, r->pool); + + + getContentType = (*env)->GetMethodID(env, servletRequestClass, SERVLETREQUEST_CONTENTTYPE_MET, STRINGRETURN_SIG); + r->content_type = fromJStringMethod(env, getContentType, servletRequest, r->pool); + + + lng = apr_table_get(r->headers_in, "Content-Languages"); + if(lng != NULL) + { + r->content_languages = apr_array_make(r->pool, 1, sizeof(const char *)); + *(const char **)apr_array_push(r->content_languages) = lng; + } + + getMethod = (*env)->GetMethodID(env, httpServletRequestClass, HTTPSERVLETREQUEST_METHOD_MET, STRINGRETURN_SIG); + method = fromJStringMethod(env, getMethod, httpServletRequest, r->pool); + + //#define SETMETHOD(m) if(strcmp(method,#m) == 0){ r->method = method; r->method_number = M_##m; } + + r->method = "INVALID"; + r->method_number = M_INVALID; + + //might be faster with elseif + if (strcmp(method, "OPTIONS") == 0) { r->method = method; r->method_number = M_OPTIONS; } + else if (strcmp(method, "GET") == 0) { r->method = method; r->method_number = M_GET; } + else if (strcmp(method, "POST") == 0) { r->method = method; r->method_number = M_POST; } + else if (strcmp(method, "PUT") == 0) { r->method = method; r->method_number = M_PUT; } + else if (strcmp(method, "DELETE") == 0) { r->method = method; r->method_number = M_DELETE; } + else if (strcmp(method, "TRACE") == 0) { r->method = method; r->method_number = M_TRACE; } + else if (strcmp(method, "CONNECT") == 0) { r->method = method; r->method_number = M_CONNECT; } + else if (strcmp(method, "MOVE") == 0) { r->method = method; r->method_number = M_MOVE; } + else if (strcmp(method, "COPY") == 0) { r->method = method; r->method_number = M_COPY; } + else if (strcmp(method, "PROPFIND") == 0) { r->method = method; r->method_number = M_PROPFIND; } + else if (strcmp(method, "PROPPATCH") == 0) { r->method = method; r->method_number = M_PROPPATCH; } + else if (strcmp(method, "MKCOL") == 0) { r->method = method; r->method_number = M_MKCOL; } + else if (strcmp(method, "LOCK") == 0) { r->method = method; r->method_number = M_LOCK; } + else if (strcmp(method, "UNLOCK") == 0) { r->method = method; r->method_number = M_UNLOCK; } + + getProtocol = (*env)->GetMethodID(env, httpServletRequestClass, HTTPSERVLETREQUEST_PROTOCOL_MET, STRINGRETURN_SIG); + r->protocol = fromJStringMethod(env, getProtocol, httpServletRequest, r->pool); + + r->request_time = apr_time_now(); + + r->parsed_uri.scheme = (char*) "http"; + r->parsed_uri.path = r->path_info; + r->parsed_uri.hostname = (char *)r->hostname; + r->parsed_uri.is_initialized = 1; + r->parsed_uri.port = port; + r->parsed_uri.port_str = port_str; + r->parsed_uri.query = r->args; + r->parsed_uri.dns_looked_up = 0; + r->parsed_uri.dns_resolved = 0; + r->parsed_uri.password = NULL; + r->parsed_uri.user = NULL; + r->parsed_uri.fragment = NULL; + + + //the request Url is in a StringBuffer object + getRequestURL = (*env)->GetMethodID(env, httpServletRequestClass, HTTPSERVLETREQUEST_REQUESTURL_MET, HTTPSERVLETREQUEST_REQUESTURL_SIG); + stringBuffer = (*env)->CallObjectMethod(env, httpServletRequest, getRequestURL); + if (stringBuffer != NULL) + { + toStringBuff = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, stringBuffer), TOSTRING_MET, STRINGRETURN_SIG); + url = fromJStringMethod(env, toStringBuff, stringBuffer, r->pool); + + if (strcmp(r->args, "") != 0) + { + len = strlen(url) + 1 + strlen(r->args) + 1; + r->unparsed_uri = (char*)apr_palloc(r->pool, len); //unparsed uri with full query + strcpy(r->unparsed_uri, url); + strcat(r->unparsed_uri, "?"); + strcat(r->unparsed_uri, r->args); + r->unparsed_uri[len] = 0; + } + else + { + r->unparsed_uri = url; + } + r->uri = r->unparsed_uri; + } + + len = strlen(r->method) + 1 + strlen(r->uri) + 1 + strlen(r->protocol) + 1; + r->the_request = (char *)apr_palloc(r->pool, len); + + strcpy(r->the_request, r->method); + strcat(r->the_request, " "); + strcat(r->the_request, r->uri); + strcat(r->the_request, " "); + strcat(r->the_request, r->protocol); + r->the_request[len] = 0; + + apr_table_setn(r->subprocess_env, "UNIQUE_ID", reqID); + + getRemoteAddr = (*env)->GetMethodID(env, servletRequestClass, SERVLETREQUEST_REMOTEADDR_MET, STRINGRETURN_SIG); + remoteAddrJStr = (jstring) (*env)->CallObjectMethod(env, servletRequest, getRemoteAddr); + remoteAddr = fromJString(env, remoteAddrJStr, r->pool); + //apr_pool_cleanup_register(r->pool, remoteAddr, memCleanup, apr_pool_cleanup_null); + +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 + c->remote_addr = CopySockAddr(modSecurityClass, env, r->pool, remoteAddr, remoteAddrJStr); + c->remote_ip = remoteAddr; +#else + c->client_addr = CopySockAddr(modSecurityClass, env, r->pool, remoteAddr, remoteAddrJStr); + c->client_ip = remoteAddr; +#endif + c->remote_host = NULL; + + status = modsecProcessRequest(r); + + removeJavaServletContext(r, JAVASERVLET_INSTREAM); + removeJavaServletContext(r, JAVASERVLET_TRANSACTION); + + if (status != DECLINED) //Java modsecurityFilter blocks the request, onResponse will not be called, it's safe to finish the request + { + apr_table_unset(requests, reqID); + modsecFinishRequest(r); + } + + return status; +} + + + +JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onResponse(JNIEnv *env, jobject obj, jobject httpTransaction) +{ + jclass httpTransactionClass; + jmethodID getTransactionID; + jstring reqIDjStr; + const char *reqID; + request_rec *r; + jmethodID getHttpResponse; + jobject httpServletResponse; + jclass httpServletResponseClass; + jmethodID getOutputStream; + jobject responseStream; + jmethodID getContentType; + jmethodID getCharEncoding; + char *ct; + const char *lng; + int status; + + + httpTransactionClass = (*env)->GetObjectClass(env, httpTransaction); + + getTransactionID = (*env)->GetMethodID(env, httpTransactionClass, HTTPTRANSACTION_TRANSACTIONID_MET, STRINGRETURN_SIG); + reqIDjStr = (jstring) (*env)->CallObjectMethod(env, httpTransaction, getTransactionID); + reqID = (*env)->GetStringUTFChars(env, reqIDjStr, NULL); + + r = (request_rec*) apr_table_get(requests, reqID); + apr_table_unset(requests, reqID); //remove this request from the requests table + (*env)->ReleaseStringUTFChars(env, reqIDjStr, reqID); + + if (r == NULL) + { + return DONE; + } + + getHttpResponse = (*env)->GetMethodID(env, httpTransactionClass, HTTPTRANSACTION_MSHTTPRESPONSE_MET, HTTPTRANSACTION_MSHTTPRESPONSE_SIG); + httpServletResponse = (*env)->CallObjectMethod(env, httpTransaction, getHttpResponse); + + httpServletResponseClass = (*env)->GetObjectClass(env, httpServletResponse); //MsHttpServletResponse class + //jclass modSecurityClass = (*env)->GetObjectClass(obj); //ModSecurity class + + getOutputStream = (*env)->GetMethodID(env, httpServletResponseClass, MSSERVLETRESPONSE_OUTPUTSTREAM_MET, MSSERVLETRESPONSE_OUTPUTSTREAM_SIG); + responseStream = (*env)->CallObjectMethod(env, httpServletResponse, getOutputStream); //Response output stream used in the read response callback + + if ((*env)->ExceptionCheck(env) == JNI_TRUE) + { + modsecFinishRequest(r); + return DONE; + } + + storeJavaServletContext(r, JAVASERVLET_TRANSACTION, httpTransaction); + storeJavaServletContext(r, JAVASERVLET_OUTSTREAM, responseStream); + + getContentType = (*env)->GetMethodID(env, httpServletResponseClass, SERVLETRESPONSE_CONTENTTYPE_MET, STRINGRETURN_SIG); + ct = fromJStringMethod(env, getContentType, httpServletResponse, r->pool); + if(strcmp(ct, "") == 0) + ct = (char*) "text/html"; + r->content_type = ct; + + + getCharEncoding = (*env)->GetMethodID(env, httpServletResponseClass, SERVLETRESPONSE_CHARENCODING_MET, STRINGRETURN_SIG); + r->content_encoding = fromJStringMethod(env, getCharEncoding, httpServletResponse, r->pool); + + + setHeaders(env, httpServletResponseClass, httpServletResponse, r->headers_out, r->pool, MODSECURITY__HTTPRESHEADERS_MET, MODSECURITY__HTTPRESHEADERS_SIG); + + lng = apr_table_get(r->headers_out, "Content-Languages"); + if(lng != NULL) + { + r->content_languages = apr_array_make(r->pool, 1, sizeof(const char *)); + *(const char **)apr_array_push(r->content_languages) = lng; + } + + + status = modsecProcessResponse(r); + + removeJavaServletContext(r, JAVASERVLET_OUTSTREAM); + removeJavaServletContext(r, JAVASERVLET_TRANSACTION); + modsecFinishRequest(r); + + return status; +}