Java test WebApp

This commit is contained in:
Mihai Pitu
2013-07-05 14:22:20 +01:00
committed by Felipe Zimmerle
parent a6c1627987
commit b9080aad18
11 changed files with 731 additions and 133 deletions

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="ModSecurityTestApp" default="default" basedir=".">
<description>Builds, tests, and runs the project ModSecurityTestApp.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-dist: called before archive building
-post-dist: called after archive building
-post-clean: called after cleaning build products
-pre-run-deploy: called before deploying
-post-run-deploy: called after deploying
Example of pluging an obfuscator after the compilation could look like
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Other way how to customize the build is by overriding existing main targets.
The target of interest are:
init-macrodef-javac: defines macro for javac compilation
init-macrodef-junit: defines macro for junit execution
init-macrodef-debug: defines macro for class debugging
do-dist: archive building
run: execution of project
javadoc-build: javadoc generation
Example of overriding the target for project execution could look like
<target name="run" depends="<PROJNAME>-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that overridden target depends on jar target and not only on
compile target as regular run target does. Again, for list of available
properties which you can use check the target you are overriding in
nbproject/build-impl.xml file.
-->
</project>

View File

@@ -0,0 +1,2 @@
Manifest-Version: 1.0

View File

@@ -0,0 +1,126 @@
package org.modsecurity;
import java.io.File;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public final class ModSecurity {
public static final int DONE = -2;
public static final int DECLINED = -1;
public static final int OK = 0;
//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
private FilterConfig filterConfig;
private String confFilename;
private long confTime;
private final static String pathToLib = "c:\\work\\mod_security\\java\\Debug\\";
static {
try {
//TODO: bad practice, native libraries should be loaded in server's classloader
System.load("c:\\work\\mod_security\\java\\libs\\zlib1.dll");
System.load("c:\\work\\mod_security\\java\\libs\\libxml2.dll");
System.load("c:\\work\\mod_security\\java\\libs\\pcre.dll");
System.load("c:\\work\\mod_security\\java\\libs\\libapr-1.dll");
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");
} catch (UnsatisfiedLinkError err) {
err.printStackTrace();
}
//java.lang.reflect.Field loadedLibraries = ClassLoader.class.getDeclaredField("loadedLibraryNames");
//loadedLibraries.setAccessible(true);
//final Vector<String> libraries = (Vector<String>) loadedLibraries.get(ClassLoader.getSystemClassLoader());
}
public ModSecurity(FilterConfig fc, String confFile) throws ServletException {
this.filterConfig = fc;
this.confFilename = confFile;
confTime = new File(confFilename).lastModified();
this.initialize();
filterConfig.getServletContext().log("ModSecurity started.");
}
private native int initialize();
public native int destroy();
public native int onRequest(String config, ServletRequest request, HttpServletRequest httprequest, String requestID, boolean reloadConfig);
public native int onResponse(ServletResponse response, HttpServletResponse htttpResponse, String requestID);
public static String[][] getHttpRequestHeaders(HttpServletRequest req) {
ArrayList<String> aList = Collections.list(req.getHeaderNames());
String[][] result = new String[aList.size()][2];
for (int i = 0; i < aList.size(); i++) {
result[i][0] = aList.get(i);
result[i][1] = req.getHeader(aList.get(i));
}
return result;
}
public static String[][] getHttpResponseHeaders(HttpServletResponse resp) {
Collection<String> headerNames = resp.getHeaderNames();
String[][] result = new String[headerNames.size()][2];
int i = 0;
for (String headerName : headerNames) {
result[i][0] = headerName;
result[i][1] = resp.getHeader(headerName);
i++;
}
return result;
}
public static boolean isIPv6(String addr) {
try {
InetAddress inetAddress = InetAddress.getByName(addr);
if (inetAddress instanceof Inet6Address) {
return true;
} else {
return false;
}
} catch (UnknownHostException ex) {
return false;
}
}
public void log(int level, String msg) {
//if (level == 1) {
filterConfig.getServletContext().log(msg);
//}
}
public boolean checkModifiedConfig() {
if (new File(confFilename).lastModified() > confTime) {
return true;
} else {
return false;
}
}
public String getAppPath() {
return filterConfig.getServletContext().getContextPath();
}
public String getConfFilename() {
return this.confFilename;
}
}

View File

@@ -0,0 +1,58 @@
package org.modsecurity;
import java.io.IOException;
import java.util.UUID;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* Docs: http://docs.oracle.com/javaee/6/tutorial/doc/bnagb.html
*/
public class ModSecurityFilter implements Filter {
ModSecurity modsecurity;
@Override
public void init(FilterConfig fc) throws ServletException {
String confFilename = fc.getInitParameter("conf");
if (confFilename == null) {
throw new ServletException("ModSecurity: parameter 'conf' not available in web.xml");
} else {
confFilename = fc.getServletContext().getRealPath(confFilename);
}
modsecurity = new ModSecurity(fc, confFilename);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain fc) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpResp = (HttpServletResponse) response;
String requestID = UUID.randomUUID().toString();
int status = modsecurity.onRequest(modsecurity.getConfFilename(), request, httpReq, requestID, modsecurity.checkModifiedConfig());
if (status != ModSecurity.DECLINED) {
return;
}
fc.doFilter(request, response);
status = modsecurity.onResponse(response, httpResp, requestID);
}
@Override
public void destroy() {
if (modsecurity != null) {
modsecurity.destroy();
}
}
}

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/ModSecurityTestApp"/>

View File

@@ -0,0 +1,12 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<p><%= request.getParameter( "data" ) %></p>
</body>
</html>

View File

@@ -0,0 +1,214 @@
# based on modsecurity.conf-recommended
# -- Rule engine initialization ----------------------------------------------
# Enable ModSecurity, attaching it to every transaction. Use detection
# only to start with, because that minimises the chances of post-installation
# disruption.
#
SecRuleEngine On
# -- Request body handling ---------------------------------------------------
# Allow ModSecurity to access request bodies. If you don't, ModSecurity
# won't be able to see any POST parameters, which opens a large security
# hole for attackers to exploit.
#
SecRequestBodyAccess On
# Enable XML request body parser.
# Initiate XML Processor in case of xml content-type
#
SecRule REQUEST_HEADERS:Content-Type "text/xml" \
"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
# Maximum request body size we will accept for buffering. If you support
# file uploads then the value given on the first line has to be as large
# as the largest file you are willing to accept. The second value refers
# to the size of data, with files excluded. You want to keep that value as
# low as practical.
#
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
# Store up to 128 KB of request body data in memory. When the multipart
# parser reachers this limit, it will start using your hard disk for
# storage. That is slow, but unavoidable.
#
SecRequestBodyInMemoryLimit 131072
# What do do if the request body size is above our configured limit.
# Keep in mind that this setting will automatically be set to ProcessPartial
# when SecRuleEngine is set to DetectionOnly mode in order to minimize
# disruptions when initially deploying ModSecurity.
#
SecRequestBodyLimitAction Reject
# 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_ERROR "!@eq 0" \
"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',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" \
"id:'200002',phase:2,t:none,log,deny,status:44, \
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_MISSING_SEMICOLON}, \
IQ %{MULTIPART_INVALID_QUOTING}, \
IP %{MULTIPART_INVALID_PART}, \
IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
# Did we see anything that might be a boundary?
#
SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \
"id:'200003',phase:2,t:none,log,deny,status:44,msg:'Multipart parser detected a possible unmatched boundary.'"
# PCRE Tuning
# We want to avoid a potential RegEx DoS condition
#
SecPcreMatchLimit 1000
SecPcreMatchLimitRecursion 1000
# 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" \
"id:'200004',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
# -- Response body handling --------------------------------------------------
# Allow ModSecurity to access response bodies.
# You should have this directive enabled in order to identify errors
# and data leakage issues.
#
# Do keep in mind that enabling this directive does increases both
# memory consumption and response latency.
#
#SecResponseBodyAccess On
# Which response MIME types do you want to inspect? You should adjust the
# configuration below to catch documents but avoid static files
# (e.g., images and archives).
#
SecResponseBodyMimeType text/plain text/html text/xml
# Buffer response bodies of up to 512 KB in length.
SecResponseBodyLimit 524288
# What happens when we encounter a response body larger than the configured
# limit? By default, we process what we have and let the rest through.
# That's somewhat less secure, but does not break any legitimate pages.
#
SecResponseBodyLimitAction ProcessPartial
# -- Filesystem configuration ------------------------------------------------
# The location where ModSecurity stores temporary files (for example, when
# it needs to handle a file upload that is larger than the configured limit).
#
# This default setting is chosen due to all systems have /tmp available however,
# this is less than ideal. It is recommended that you specify a location that's private.
#
SecTmpDir c:\inetpub\temp\
# The location where ModSecurity will keep its persistent data. This default setting
# is chosen due to all systems have /tmp available however, it
# too should be updated to a place that other users can't access.
#
SecDataDir c:\inetpub\temp\
# -- File uploads handling configuration -------------------------------------
# The location where ModSecurity stores intercepted uploaded files. This
# location must be private to ModSecurity. You don't want other users on
# the server to access the files, do you?
#
#SecUploadDir /opt/modsecurity/var/upload/
# By default, only keep the files that were determined to be unusual
# in some way (by an external inspection script). For this to work you
# will also need at least one file inspection rule.
#
#SecUploadKeepFiles RelevantOnly
# Uploaded files are by default created with permissions that do not allow
# any other user to access them. You may need to relax that if you want to
# interface ModSecurity to an external program (e.g., an anti-virus).
#
#SecUploadFileMode 0600
# -- Debug log configuration -------------------------------------------------
# The default debug log configuration is to duplicate the error, warning
# and notice messages from the error log.
#
#SecDebugLog /opt/modsecurity/var/log/debug.log
#SecDebugLogLevel 3
# -- Audit log configuration -------------------------------------------------
# Log the transactions that are marked by a rule, as well as those that
# trigger a server error (determined by a 5xx or 4xx, excluding 404,
# level response status codes).
#
#SecAuditEngine RelevantOnly
#SecAuditLogRelevantStatus "^(?:5|4(?!04))"
# Log everything we know about a transaction.
#SecAuditLogParts ABIJDEFHZ
# Use a single file for logging. This is much easier to look at, but
# assumes that you will use the audit log only ocassionally.
#
#SecAuditLogType Serial
#SecAuditLog c:\inetpub\log\modsec_audit.log
# Specify the path for concurrent audit logging.
#SecAuditLogStorageDir c:\inetpub\log\
# -- Miscellaneous -----------------------------------------------------------
# Use the most commonly used application/x-www-form-urlencoded parameter
# separator. There's probably only one application somewhere that uses
# something else so don't expect to change this value.
#
SecArgumentSeparator &
# Settle on version 0 (zero) cookies, as that is what most applications
# use. Using an incorrect cookie version may open your installation to
# evasion attacks (against the rules that examine named cookies).
#
SecCookieFormat 0
# Specify your Unicode Code Point.
# This mapping is used by the t:urlDecodeUni transformation function
# to properly map encoded data to your language. Properly setting
# these directives helps to reduce false positives and negatives.
#
#SecUnicodeCodePage 20127
#SecUnicodeMapFile unicode.mapping

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>ModSecurity for Java</display-name>
<filter>
<filter-name>ModSecurityFilter</filter-name>
<filter-class>org.modsecurity.ModSecurityFilter</filter-class>
<init-param>
<param-name>conf</param-name>
<param-value>/WEB-INF/modsecurity.conf</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ModSecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

View File

@@ -0,0 +1,14 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<form method="post" action="Post.jsp">
Post Action: <input type="text" name="data" />
<input type="submit" />
</form>
</body>
</html>

View File

@@ -17,10 +17,10 @@
#define MODSECURITY_LOG_MET "log"
#define MODSECURITY_LOG_SIG "(ILjava/lang/String;)V"
#define MODSECURITY__HTTPHEADERS_MET "getHttpHeaders"
#define MODSECURITY__HTTPHEADERS_SIG "(Ljavax/servlet/http/HttpServletRequest;)[[Ljava/lang/String;"
//#define MODSECURITY__REQUESTBODY_MET "getRequestBody"
//#define MODSECURITY__REQUESTBODY_SIG "(Ljavax/servlet/http/HttpServletRequest;)Ljava/lang/String;"
#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"
@@ -39,11 +39,14 @@
#define HTTPSERVLETREQUEST_METHOD_MET "getMethod"
#define HTTPSERVLETREQUEST_PROTOCOL_MET "getProtocol"
#define HTTPSERVLETREQUEST_REQUESTURL_MET "getRequestURL"
#define HTTPSERVLETREQUEST_REQUESTURL_SIG "()Ljava/lang/StringBuffer;"
#define SERVLETRESPONSE_CONTENTTYPE_MET "getContentType"
#define SERVLETRESPONSE_CHARENCODING_MET "getCharacterEncoding"
//typedef struct {
JavaVM *jvm;
@@ -52,11 +55,16 @@ directory_config *config;
//} JavaModSecurityContext;
jmethodID logMethod;
#define JAVASERVLET_CONTEXT "JavaServletContext"
apr_table_t *requests;
apr_pool_t *requestsPool;
#define JAVASERVLET_INSTREAM "RequestInStream"
void storeJavaServletContext(request_rec *r, jobject obj)
{
apr_table_setn(r->notes, JAVASERVLET_CONTEXT, (const char *)obj);
apr_table_setn(r->notes, JAVASERVLET_INSTREAM, (const char *)obj);
}
jobject getJavaServletContext(request_rec *r)
@@ -65,14 +73,14 @@ jobject getJavaServletContext(request_rec *r)
request_rec *rx = NULL;
/* Look in the current request first. */
obj = (jobject)apr_table_get(r->notes, JAVASERVLET_CONTEXT);
obj = (jobject)apr_table_get(r->notes, JAVASERVLET_INSTREAM);
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, JAVASERVLET_CONTEXT);
obj = (jobject)apr_table_get(r->main->notes, JAVASERVLET_INSTREAM);
if (obj != NULL)
{
return obj;
@@ -83,7 +91,7 @@ jobject getJavaServletContext(request_rec *r)
rx = r->prev;
while(rx != NULL)
{
obj = (jobject)apr_table_get(rx->notes, JAVASERVLET_CONTEXT);
obj = (jobject)apr_table_get(rx->notes, JAVASERVLET_INSTREAM);
if (obj != NULL)
{
return obj;
@@ -94,11 +102,11 @@ jobject getJavaServletContext(request_rec *r)
return NULL;
}
apr_status_t memCleanup(void *mem)
{
free(mem);
return APR_SUCCESS;
}
//apr_status_t memCleanup(void *mem)
//{
// free(mem);
// return APR_SUCCESS;
//}
apr_sockaddr_t *CopySockAddr(jclass msClass, JNIEnv *env, apr_pool_t *pool, char *addrstr, jstring addrStrJstr)
{
@@ -142,24 +150,30 @@ apr_sockaddr_t *CopySockAddr(jclass msClass, JNIEnv *env, apr_pool_t *pool, char
return addr;
}
char* fromJStringMethod(JNIEnv *env, jmethodID method, jobject obj, request_rec *r)
inline char* fromJString(JNIEnv *env, jstring jStr, apr_pool_t *pool)
{
char *str;
jstring jStr = (jstring) (env)->CallObjectMethod(obj, method);
if (jStr != NULL)
{
str = (char*) (env)->GetStringUTFChars(jStr, 0);
(env)->ReleaseStringUTFChars(jStr, str);
const char *jCStr = (env)->GetStringUTFChars(jStr, NULL);
int len = strlen(jCStr);
str = (char*) apr_palloc(pool, len + 1);
memcpy(str, jCStr, len);
str[len] = '\0'; //null terminate
(env)->ReleaseStringUTFChars(jStr, jCStr); //release java heap memory
}
else
str = "";
apr_pool_cleanup_register(r->pool, str, memCleanup, apr_pool_cleanup_null);
return str;
}
inline char* fromJStringMethod(JNIEnv *env, jmethodID method, jobject obj, apr_pool_t *pool)
{
jstring jStr = (jstring) (env)->CallObjectMethod(obj, method);
return fromJString(env, jStr, pool);
}
void logSec(void *obj, int level, char *str)
@@ -167,7 +181,7 @@ void logSec(void *obj, int level, char *str)
JNIEnv *env;
jstring jStr;
if (!(jvm)->AttachCurrentThread((void **)&env, NULL))
if (!(jvm)->AttachCurrentThread((void **)&env, NULL)) //get the Enviroment from the JavaVM
{
jStr = (env)->NewStringUTF(str);
@@ -182,7 +196,7 @@ void logSec(void *obj, int level, char *str)
apr_status_t ReadBodyCallback(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)
{
jobject inputStream = getJavaServletContext(r);
jobject inputStream = getJavaServletContext(r); //servlet request input stream
JNIEnv *env;
*readcnt = 0;
@@ -195,12 +209,14 @@ apr_status_t ReadBodyCallback(request_rec *r, char *buf, unsigned int length, un
if (!(jvm)->AttachCurrentThread((void **)&env, NULL))
{
jclass inputStreamClass = env->FindClass(SERVLETINPUTSTREAM_JAVACLASS);
//read request body from the servlet input stream using 'read' method
jclass inputStreamClass = env->FindClass(SERVLETINPUTSTREAM_JAVACLASS);
jmethodID read = (env)->GetMethodID(inputStreamClass, INPUTSTREAM_READ_MET, INPUTSTREAM_READ_SIG);
jbyteArray byteArrayBuf = (env)->NewByteArray(length);
jint count = (env)->CallIntMethod(inputStream, read, byteArrayBuf, 0, length);
jbyte* bufferPtr = (env)->GetByteArrayElements(byteArrayBuf, NULL);
if (count == -1 || count > length || env->ExceptionCheck() == JNI_TRUE) //end of stream
{
@@ -210,13 +226,10 @@ apr_status_t ReadBodyCallback(request_rec *r, char *buf, unsigned int length, un
{
*readcnt = count;
jbyte* bufferPtr = (env)->GetByteArrayElements(byteArrayBuf, NULL);
memcpy(buf, bufferPtr, *readcnt);
(env)->ReleaseByteArrayElements(byteArrayBuf, bufferPtr, NULL);
(env)->DeleteLocalRef(byteArrayBuf);
}
(env)->ReleaseByteArrayElements(byteArrayBuf, bufferPtr, NULL);
(env)->DeleteLocalRef(byteArrayBuf);
(jvm)->DetachCurrentThread();
}
@@ -242,9 +255,9 @@ apr_status_t WriteResponseCallback(request_rec *r, char *buf, unsigned int lengt
JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_initialize(JNIEnv *env, jobject obj)
{
(env)->GetJavaVM(&jvm);
modSecurityInstance = (env)->NewGlobalRef(obj);
//modSecurityClass = env->GetObjectClass(obj);
logMethod = (env)->GetMethodID(env->GetObjectClass(obj), MODSECURITY_LOG_MET, MODSECURITY_LOG_SIG);
modSecurityInstance = (env)->NewGlobalRef(obj); //Save the ModSecurity object for further use
logMethod = (env)->GetMethodID(env->GetObjectClass(obj), MODSECURITY_LOG_MET, MODSECURITY_LOG_SIG); //log method ID
modsecSetLogHook(NULL, logSec);
@@ -254,107 +267,37 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_initialize(JNIEnv *env,
modsecSetWriteResponse(WriteResponseCallback);
modsecInit();
//char *compname = (char *)malloc(128);
//s->server_hostname = compname;
modsecStartConfig();
config = modsecGetDefaultConfig();
modsecFinalizeConfig();
modsecInitProcess();
config = NULL;
//table for requests
apr_pool_create(&requestsPool, NULL);
requests = apr_table_make(requestsPool, 10);
return APR_SUCCESS;
}
JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_destroy(JNIEnv *env, jobject obj)
{
(env)->DeleteGlobalRef(modSecurityInstance);
//(env)->DeleteGlobalRef(modSecurityClass);
apr_pool_destroy(requestsPool);
modsecTerminate();
return APR_SUCCESS;
}
//int getPort(request_rec *r)
//{
// int port = 0;
// char *port_str = NULL;
//
// if(r->hostname != NULL)
// {
// int k = 0;
// char *ptr = (char *)r->hostname;
//
// while(*ptr != 0 && *ptr != ':')
// ptr++;
//
// if(*ptr == ':')
// {
// *ptr = 0;
// port_str = ptr + 1;
// port = atoi(port_str);
// }
// }
// return port;
//}
JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, jobject obj, jstring configPath, jobject servletRequest, jobject httpServletRequest, jstring requestID, jboolean reloadConfig)
inline void setHeaders(JNIEnv *env, jclass modSecurityClass, jobject httpServletR, apr_table_t *reqHeaders, apr_pool_t *pool, const char *headersMet, const char *headersSig)
{
conn_rec *c;
request_rec *r;
const char *path = (env)->GetStringUTFChars(configPath, NULL);
const char *reqID = (env)->GetStringUTFChars(requestID, NULL);
if (config == NULL || reloadConfig)
{
config = modsecGetDefaultConfig();
const char *err = modsecProcessConfig(config, path, NULL);
if(err != NULL)
{
logSec(NULL, 0, (char*)err);
return DONE;
}
}
c = modsecNewConnection();
modsecProcessConnection(c);
r = modsecNewRequest(c, config);
jclass httpServletRequestClass = env->GetObjectClass(httpServletRequest);
jclass servletRequestClass = env->GetObjectClass(servletRequest);
jclass modSecurityClass = env->GetObjectClass(obj);
jmethodID getInputStream = (env)->GetMethodID(servletRequestClass, SERVLETREQUEST_INPUTSTREAM_MET, SERVLETREQUEST_INPUTSTREAM_SIG);
jobject inputStream = (env)->CallObjectMethod(servletRequest, getInputStream);
//jobject gref = env->NewGlobalRef(inputStream);
//apr_pool_cleanup_register(r->pool, gref, jDeleteGlobalRef, apr_pool_cleanup_null);
storeJavaServletContext(r, inputStream);
jmethodID getServerName = (env)->GetMethodID(servletRequestClass, SERVLETREQUEST_SERVERNAME_MET, STRINGRETURN_SIG);
r->hostname = fromJStringMethod(env, getServerName, servletRequest, r);
jmethodID getServerPort = (env)->GetMethodID(servletRequestClass, SERVLETREQUEST_SERVERPORT_MET, SERVLETREQUEST_SERVERPORT_SIG);
int port = (env)->CallIntMethod(servletRequest, getServerPort);
size_t len = (size_t) ceil(log10((float) abs(port)));
char *port_str = (char*) apr_palloc(r->pool, len);
itoa(port, port_str, 10);
jmethodID getPathInfo = (env)->GetMethodID(httpServletRequestClass, HTTPSERVLETREQUEST_PATHINFO_MET, STRINGRETURN_SIG);
r->path_info = fromJStringMethod(env, getPathInfo, httpServletRequest, r);
jmethodID getQueryString = (env)->GetMethodID(httpServletRequestClass, HTTPSERVLETREQUEST_QUERYSTRING_MET, STRINGRETURN_SIG);
r->args = fromJStringMethod(env, getQueryString, httpServletRequest, r);
jmethodID getHttpHeaders = (env)->GetStaticMethodID(modSecurityClass, MODSECURITY__HTTPHEADERS_MET, MODSECURITY__HTTPHEADERS_SIG);
jobjectArray headersTable = (jobjectArray) (env)->CallStaticObjectMethod(modSecurityClass, getHttpHeaders, httpServletRequest);
//All headers are returned in a table by a static method from ModSecurity class
jmethodID getHttpHeaders = (env)->GetStaticMethodID(modSecurityClass, headersMet, headersSig);
jobjectArray headersTable = (jobjectArray) (env)->CallStaticObjectMethod(modSecurityClass, getHttpHeaders, httpServletR);
jsize size = (env)->GetArrayLength(headersTable);
for (int i = 0; i < size; i++)
@@ -367,26 +310,88 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, j
if (headerNameJStr != NULL && headerValueJStr != NULL)
{
headerName = (env)->GetStringUTFChars(headerNameJStr, 0);
apr_pool_cleanup_register(r->pool, headerName, memCleanup, apr_pool_cleanup_null);
headerName = fromJString(env, headerNameJStr, pool);
//apr_pool_cleanup_register(r->pool, headerName, memCleanup, apr_pool_cleanup_null);
headerValue = (env)->GetStringUTFChars(headerValueJStr, 0);
apr_pool_cleanup_register(r->pool, headerValue, 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(r->headers_in, headerName, headerValue);
apr_table_setn(reqHeaders, headerName, headerValue);
(env)->ReleaseStringUTFChars(headerNameJStr, headerName);
(env)->ReleaseStringUTFChars(headerValueJStr, headerValue);
env->DeleteLocalRef(headerNameJStr);
env->DeleteLocalRef(headerValueJStr);
}
}
}
JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, jobject obj, jstring configPath, jobject servletRequest, jobject httpServletRequest, jstring requestID, jboolean reloadConfig)
{
//critical section ?
conn_rec *c;
request_rec *r;
const char *path = (env)->GetStringUTFChars(configPath, NULL); //path to modsecurity.conf
if (config == NULL || reloadConfig)
{
config = modsecGetDefaultConfig();
const char *err = modsecProcessConfig(config, path, NULL);
if(err != NULL)
{
logSec(NULL, 0, (char*)err);
(env)->ReleaseStringUTFChars(configPath, path);
return DONE;
}
}
c = modsecNewConnection();
modsecProcessConnection(c);
r = modsecNewRequest(c, config);
const char *reqID = fromJString(env, requestID, r->pool); //unique ID of this request
apr_table_setn(requests, reqID, (const char*) r); //store this request for response processing
jclass httpServletRequestClass = env->GetObjectClass(httpServletRequest); //HttpServletRequest interface
jclass servletRequestClass = env->GetObjectClass(servletRequest); //ServletRequest interface
jclass modSecurityClass = env->GetObjectClass(obj); //ModSecurity class
jmethodID getInputStream = (env)->GetMethodID(servletRequestClass, SERVLETREQUEST_INPUTSTREAM_MET, SERVLETREQUEST_INPUTSTREAM_SIG);
jobject inputStream = (env)->CallObjectMethod(servletRequest, getInputStream); //Request body input stream used in the read body callback
storeJavaServletContext(r, inputStream);
jmethodID getServerName = (env)->GetMethodID(servletRequestClass, SERVLETREQUEST_SERVERNAME_MET, STRINGRETURN_SIG);
r->hostname = fromJStringMethod(env, getServerName, servletRequest, r->pool);
jmethodID getServerPort = (env)->GetMethodID(servletRequestClass, SERVLETREQUEST_SERVERPORT_MET, SERVLETREQUEST_SERVERPORT_SIG);
int port = (env)->CallIntMethod(servletRequest, getServerPort); //server port
size_t len = (size_t) ceil(log10((float) port));
char *port_str = (char*) apr_palloc(r->pool, len);
itoa(port, port_str, 10);
jmethodID getPathInfo = (env)->GetMethodID(httpServletRequestClass, HTTPSERVLETREQUEST_PATHINFO_MET, STRINGRETURN_SIG);
r->path_info = fromJStringMethod(env, getPathInfo, httpServletRequest, r->pool);
jmethodID getQueryString = (env)->GetMethodID(httpServletRequestClass, HTTPSERVLETREQUEST_QUERYSTRING_MET, STRINGRETURN_SIG);
r->args = fromJStringMethod(env, getQueryString, httpServletRequest, r->pool);
setHeaders(env, modSecurityClass, httpServletRequest, r->headers_in, r->pool, MODSECURITY__HTTPREQHEADERS_MET, MODSECURITY__HTTPREQHEADERS_SIG);
jmethodID getCharacterEncoding = (env)->GetMethodID(servletRequestClass, SERVLETREQUEST_CHARENCODING_MET, STRINGRETURN_SIG);
r->content_encoding = fromJStringMethod(env, getCharacterEncoding, servletRequest, r);
r->content_encoding = fromJStringMethod(env, getCharacterEncoding, servletRequest, r->pool);
jmethodID getContentType = (env)->GetMethodID(servletRequestClass, SERVLETREQUEST_CONTENTTYPE_MET, STRINGRETURN_SIG);
r->content_type = fromJStringMethod(env, getContentType, servletRequest, r);
r->content_type = fromJStringMethod(env, getContentType, servletRequest, r->pool);
const char *lng = apr_table_get(r->headers_in, "Content-Languages");
@@ -397,7 +402,7 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, j
}
jmethodID getMethod = (env)->GetMethodID(httpServletRequestClass, HTTPSERVLETREQUEST_METHOD_MET, STRINGRETURN_SIG);
const char* method = fromJStringMethod(env, getMethod, httpServletRequest, r);
const char* method = fromJStringMethod(env, getMethod, httpServletRequest, r->pool);
//#define SETMETHOD(m) if(strcmp(method,#m) == 0){ r->method = method; r->method_number = M_##m; }
@@ -421,7 +426,7 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, j
else if (strcmp(method, "UNLOCK") == 0) { r->method = method; r->method_number = M_UNLOCK; }
jmethodID getProtocol = (env)->GetMethodID(httpServletRequestClass, HTTPSERVLETREQUEST_PROTOCOL_MET, STRINGRETURN_SIG);
r->protocol = fromJStringMethod(env, getProtocol, httpServletRequest, r);
r->protocol = fromJStringMethod(env, getProtocol, httpServletRequest, r->pool);
r->request_time = apr_time_now();
@@ -438,12 +443,25 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, j
r->parsed_uri.user = NULL;
r->parsed_uri.fragment = NULL;
//the request Url is in a StringBuffer object
jmethodID getRequestURL = (env)->GetMethodID(httpServletRequestClass, HTTPSERVLETREQUEST_REQUESTURL_MET, HTTPSERVLETREQUEST_REQUESTURL_SIG);
jobject stringBuffer = (env)->CallObjectMethod(httpServletRequest, getRequestURL);
if (stringBuffer != NULL)
{
jmethodID toStringBuff = (env)->GetMethodID((env)->GetObjectClass(stringBuffer), TOSTRING_MET, STRINGRETURN_SIG);
r->unparsed_uri = fromJStringMethod(env, toStringBuff, stringBuffer, r);
char *url = fromJStringMethod(env, toStringBuff, stringBuffer, r->pool);
if (strcmp(r->args, "") != 0)
{
r->unparsed_uri = (char*)apr_palloc(r->pool, strlen(url) + 1 + strlen(r->args)); //unparsed uri with full query
strcpy(r->unparsed_uri, url);
strcat(r->unparsed_uri, "?");
strcat(r->unparsed_uri, r->args);
}
else
{
r->unparsed_uri = url;
}
r->uri = r->unparsed_uri;
}
@@ -460,8 +478,8 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, j
jmethodID getRemoteAddr = (env)->GetMethodID(servletRequestClass, SERVLETREQUEST_REMOTEADDR_MET, STRINGRETURN_SIG);
jstring remoteAddrJStr = (jstring) (env)->CallObjectMethod(servletRequest, getRemoteAddr);
char *remoteAddr = (char*) (env)->GetStringUTFChars(remoteAddrJStr, 0);
apr_pool_cleanup_register(r->pool, remoteAddr, memCleanup, apr_pool_cleanup_null);
char *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);
@@ -472,13 +490,58 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, j
#endif
c->remote_host = NULL;
int status = modsecProcessRequest(r);
(env)->ReleaseStringUTFChars(remoteAddrJStr, remoteAddr);
(env)->ReleaseStringUTFChars(configPath, path);
(env)->ReleaseStringUTFChars(requestID, reqID);
(env)->DeleteLocalRef(inputStream);
return status;
}
JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onResponse(JNIEnv *env, jobject obj, jobject servletResponse, jobject httpServletResponse, jstring requestID)
{
const char *reqID = env->GetStringUTFChars(requestID, NULL);
request_rec *r = (request_rec*) apr_table_get(requests, reqID);
if (r == NULL)
{
env->ReleaseStringUTFChars(requestID, reqID);
return DONE;
}
jclass httpServletResponseClass = env->GetObjectClass(httpServletResponse); //HttpServletResponse interface
jclass servletResponseClass = env->GetObjectClass(servletResponse); //ServletResponse interface
jclass modSecurityClass = env->GetObjectClass(obj); //ModSecurity class
jmethodID getContentType = (env)->GetMethodID(servletResponseClass, SERVLETRESPONSE_CONTENTTYPE_MET, STRINGRETURN_SIG);
char *ct = fromJStringMethod(env, getContentType, servletResponse, r->pool);
if(strcmp(ct, "") == 0)
ct = "text/html";
r->content_type = ct;
jmethodID getCharEncoding = (env)->GetMethodID(servletResponseClass, SERVLETRESPONSE_CHARENCODING_MET, STRINGRETURN_SIG);
r->content_encoding = fromJStringMethod(env, getCharEncoding, servletResponse, r->pool);
setHeaders(env, modSecurityClass, httpServletResponse, r->headers_out, r->pool, MODSECURITY__HTTPRESHEADERS_MET, MODSECURITY__HTTPRESHEADERS_SIG);
const char *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;
}
//modsecProcessResponse(r);
apr_table_unset(requests, reqID); //remove this request from the requests table
modsecFinishRequest(r);
env->ReleaseStringUTFChars(requestID, reqID);
return DONE;
}

View File

@@ -7,6 +7,12 @@
#ifdef __cplusplus
extern "C" {
#endif
#undef org_modsecurity_ModSecurity_DONE
#define org_modsecurity_ModSecurity_DONE -2L
#undef org_modsecurity_ModSecurity_DECLINED
#define org_modsecurity_ModSecurity_DECLINED -1L
#undef org_modsecurity_ModSecurity_OK
#define org_modsecurity_ModSecurity_OK 0L
/*
* Class: org_modsecurity_ModSecurity
* Method: initialize
@@ -31,6 +37,14 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_destroy
JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest
(JNIEnv *, jobject, jstring, jobject, jobject, jstring, jboolean);
/*
* Class: org_modsecurity_ModSecurity
* Method: onResponse
* Signature: (Ljavax/servlet/ServletResponse;Ljavax/servlet/http/HttpServletResponse;Ljava/lang/String)I
*/
JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onResponse
(JNIEnv *, jobject, jobject, jobject, jstring);
#ifdef __cplusplus
}
#endif