diff --git a/java/.gitignore b/java/.gitignore
index 74b1dadb..ed65b5f9 100644
--- a/java/.gitignore
+++ b/java/.gitignore
@@ -1,3 +1,4 @@
+libs/
Debug/
Release/
*.sdf
diff --git a/java/ModSecurityJNI.vcxproj b/java/ModSecurityJNI.vcxproj
index d25bd911..c0cc95b5 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 2c15077f..f9f2b7eb 100644
--- a/java/ModSecurityJNI.vcxproj.filters
+++ b/java/ModSecurityJNI.vcxproj.filters
@@ -96,7 +96,6 @@
ModSecurity Sources
-
Standalone Sources
@@ -118,6 +117,7 @@
Standalone Sources
+
diff --git a/java/ModSecurityJNI.vcxproj.user b/java/ModSecurityJNI.vcxproj.user
new file mode 100644
index 00000000..a375ae35
--- /dev/null
+++ b/java/ModSecurityJNI.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurity.java b/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurity.java
index e5039f91..d35327ce 100644
--- a/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurity.java
+++ b/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurity.java
@@ -12,18 +12,25 @@ 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
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 {
- //TODO: bad practice, native libraries should be loaded in server's classloader
+// try {
+// Class.forName("org.modsecurity.loader.ModSecurityLoader");
+// System.out.println("MS loader found");
+// } catch (ClassNotFoundException ex) {
+// Logger.getLogger(ModSecurity.class.getName()).log(Level.SEVERE, null, ex);
+// }
+
+ //TODO: bad practice (if we have two webapps using ModSecurity, one will raise UnsatisfiedLinkError),
+ //native libraries should be loaded in server's root 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");
@@ -31,9 +38,6 @@ 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");
- //java.lang.reflect.Field loadedLibraries = ClassLoader.class.getDeclaredField("loadedLibraryNames");
- //loadedLibraries.setAccessible(true);
- //final Vector libraries = (Vector) loadedLibraries.get(ClassLoader.getSystemClassLoader());
}
public ModSecurity(FilterConfig fc, String confFile) throws ServletException {
@@ -41,11 +45,11 @@ public final class ModSecurity {
this.confFilename = confFile;
confTime = new File(confFilename).lastModified();
- this.initialize();
+ this.initialize(fc.getFilterName());
filterConfig.getServletContext().log("ModSecurity started.");
}
- private native int initialize();
+ private native int initialize(String serverName);
public native int destroy();
diff --git a/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurityFilter.java b/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurityFilter.java
index 7a8fcbb3..afc021ee 100644
--- a/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurityFilter.java
+++ b/java/ModSecurityTestApp/src/java/org/modsecurity/ModSecurityFilter.java
@@ -23,10 +23,9 @@ public class ModSecurityFilter implements Filter {
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);
}
@@ -40,14 +39,24 @@ public class ModSecurityFilter implements Filter {
int status = modsecurity.onRequest(modsecurity.getConfFilename(), httpTran, modsecurity.checkModifiedConfig()); //modsecurity reloads only if primary config file is modified
if (status != ModSecurity.DECLINED) {
+ if (status > 0) {
+ httpTran.getHttpResponse().setStatus(status);
+ httpTran.getHttpResponse().sendError(status);
+ }
return;
}
//process request
fc.doFilter(httpTran.getMsHttpRequest(), httpTran.getMsHttpResponse());
+
status = modsecurity.onResponse(httpTran);
+ if(status != ModSecurity.OK && status != ModSecurity.DECLINED) {
+ httpTran.getMsHttpResponse().reset();
+ httpTran.getMsHttpResponse().setStatus(status);
+ }
+
} finally {
httpTran.destroy();
}
diff --git a/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletRequest.java b/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletRequest.java
index 36b2e05d..b2a8b85f 100644
--- a/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletRequest.java
+++ b/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletRequest.java
@@ -81,7 +81,7 @@ public class MsHttpServletRequest extends HttpServletRequestWrapper {
bodyFile.delete();
}
}
-
+
public static String[][] getHttpRequestHeaders(HttpServletRequest req) {
ArrayList aList = Collections.list(req.getHeaderNames());
@@ -97,7 +97,7 @@ public class MsHttpServletRequest extends HttpServletRequestWrapper {
return result;
}
-
+
public String getTmpPath() {
return tmpPath;
}
@@ -130,10 +130,28 @@ public class MsHttpServletRequest extends HttpServletRequestWrapper {
return bodyBytes;
}
- public void readBody(int maxContentLength) throws IOException, ServletException {
-
+ public void setBodyBytes(byte[] bytes) throws IOException {
String contentType = req.getContentType();
+ bodyBytes = new byte[bytes.length];
+ System.arraycopy(bytes, 0, bodyBytes, 0, bytes.length);
+ body = new String(bodyBytes, encoding);
+ if ((contentType != null) && ((contentType.compareTo("application/x-www-form-urlencoded") == 0) || (contentType.compareTo("application/x-form-urlencoded") == 0))) {
+ addUrlEncoded(body);
+ }
+ }
+
+ @Override
+ public int getContentLength() {
+ if (bodyBytes == null)
+ return req.getContentLength();
+ return bodyBytes.length;
+ }
+
+ public void readBody(int maxContentLength) throws IOException, ServletException {
+
+ String contentType = req.getContentType();
+
if ((contentType != null) && (contentType.startsWith("multipart/form-data"))) {
readBodyMultipart(maxContentLength);
} else {
@@ -222,7 +240,6 @@ public class MsHttpServletRequest extends HttpServletRequestWrapper {
}
}
-
/**
* Parses the given URL-encoded string and adds the parameters to the
* request parameter list.
diff --git a/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletResponse.java b/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletResponse.java
index 46729c18..0264f8f2 100644
--- a/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletResponse.java
+++ b/java/ModSecurityTestApp/src/java/org/modsecurity/MsHttpServletResponse.java
@@ -202,9 +202,8 @@ public class MsHttpServletResponse extends HttpServletResponseWrapper {
stream = new ByteArrayInputStream(new String(writer.toCharArray()).getBytes());
} else if (msWriter == null) {
stream = new ByteArrayInputStream(((MsOutputStream) this.getOutputStream()).toByteArray());
- } else {
-
}
+
return stream;
}
@@ -287,6 +286,16 @@ public class MsHttpServletResponse extends HttpServletResponseWrapper {
return super.isCommitted();
}
+ public void setBodyBytes(byte[] bytes) throws IOException {
+ if (msOutputStream == null) {
+ msWriter.reset();
+ msWriter.write(new String(bytes));
+ } else if (msWriter == null) {
+ msOutputStream.reset();
+ msOutputStream.write(bytes, 0, bytes.length);
+ }
+ }
+
@Override
public void reset() throws IllegalStateException {
if (interceptMode != INTERCEPT_ON) {
diff --git a/java/ModSecurityTestApp/web/WEB-INF/web.xml b/java/ModSecurityTestApp/web/WEB-INF/web.xml
index 620ad23c..1e957606 100644
--- a/java/ModSecurityTestApp/web/WEB-INF/web.xml
+++ b/java/ModSecurityTestApp/web/WEB-INF/web.xml
@@ -10,7 +10,8 @@
org.modsecurity.ModSecurityFilter
conf
- /WEB-INF/modsecurity.conf
+ c:\inetpub\wwwroot\owasp-crs\modsecurity.conf
+
diff --git a/java/org_modsecurity_ModSecurity.cpp b/java/org_modsecurity_ModSecurity.cpp
index 84040576..6b814e04 100644
--- a/java/org_modsecurity_ModSecurity.cpp
+++ b/java/org_modsecurity_ModSecurity.cpp
@@ -3,6 +3,7 @@
#include "org_modsecurity_ModSecurity.h"
#include "api.h"
+
#include
#define MODSECURITY_JAVACLASS "org/modsecurity/ModSecurity"
@@ -54,6 +55,8 @@
#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"
@@ -75,17 +78,22 @@ jmethodID logMethod;
apr_table_t *requests;
apr_pool_t *requestsPool;
+char *serverHostname;
-
-#define JAVASERVLET_INSTREAM "ReqBStr"
-#define JAVASERVLET_OUTSTREAM "ResBStr"
-
+#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;
@@ -121,11 +129,6 @@ jobject getJavaServletContext(request_rec *r, const char *key)
return NULL;
}
-//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)
{
@@ -246,6 +249,9 @@ apr_status_t ReadBodyCallback(request_rec *r, char *buf, unsigned int length, un
*readcnt = count;
memcpy(buf, bufferPtr, *readcnt);
+ //const char *test = "Foo' or '2' < '1' ;--";
+ //memcpy(buf, test, strlen(test));
+
}
(env)->ReleaseByteArrayElements(byteArrayBuf, bufferPtr, NULL);
(env)->DeleteLocalRef(byteArrayBuf);
@@ -258,6 +264,38 @@ apr_status_t ReadBodyCallback(request_rec *r, char *buf, unsigned int length, un
apr_status_t WriteBodyCallback(request_rec *r, char *buf, unsigned int length)
{
+ jobject httpTransaction = getJavaServletContext(r, JAVASERVLET_TRANSACTION);
+ JNIEnv *env;
+
+ if(httpTransaction == NULL)
+ {
+ return APR_SUCCESS;
+ }
+
+ if (!(jvm)->AttachCurrentThread((void **)&env, NULL))
+ {
+ jclass httpTransactionClass = env->GetObjectClass(httpTransaction);
+
+ jmethodID getHttpRequest = env->GetMethodID(httpTransactionClass, HTTPTRANSACTION_MSHTTPREQUEST_MET, HTTPTRANSACTION_MSHTTPREQUEST_SIG);
+ jobject httpServletRequest = env->CallObjectMethod(httpTransaction, getHttpRequest);
+
+ jclass httpServletRequestClass = env->GetObjectClass(httpServletRequest);
+ jmethodID setBodyBytes = env->GetMethodID(httpServletRequestClass, MSHTTPSERVLETREQUEST_SETBODY_MET, MSHTTPSERVLETREQUEST_SETBODY_SIG);
+
+ jbyte *jbuf = new jbyte[length];
+ for (int i = 0; i < length; i++)
+ jbuf[i] = buf[i];
+
+ jbyteArray byteArrayBuf = (env)->NewByteArray(length);
+ env->SetByteArrayRegion(byteArrayBuf, 0, length, jbuf);
+
+ //on setBodyBytes we copy buf bytes
+ env->CallVoidMethod(httpServletRequest, setBodyBytes, byteArrayBuf);
+
+ //(env)->ReleaseByteArrayElements(byteArrayBuf, jbuf, NULL);
+
+ (jvm)->DetachCurrentThread();
+ }
return APR_SUCCESS;
}
@@ -305,10 +343,43 @@ apr_status_t ReadResponseCallback(request_rec *r, char *buf, unsigned int length
apr_status_t WriteResponseCallback(request_rec *r, char *buf, unsigned int length)
{
+ JNIEnv *env;
+ jobject httpTransaction = getJavaServletContext(r, JAVASERVLET_TRANSACTION);
+
+ if(httpTransaction == NULL)
+ {
+ (jvm)->DetachCurrentThread();
+ return APR_SUCCESS;
+ }
+
+ if (!(jvm)->AttachCurrentThread((void **)&env, NULL))
+ {
+ jclass httpTransactionClass = env->GetObjectClass(httpTransaction);
+
+ jmethodID getHttpResponse = env->GetMethodID(httpTransactionClass, HTTPTRANSACTION_MSHTTPRESPONSE_MET, HTTPTRANSACTION_MSHTTPRESPONSE_SIG);
+ jobject httpServletResponse = env->CallObjectMethod(httpTransaction, getHttpResponse);
+
+ jclass httpServletResponseClass = env->GetObjectClass(httpServletResponse);
+ jmethodID setBodyBytes = env->GetMethodID(httpServletResponseClass, MSHTTPSERVLETREQUEST_SETBODY_MET, MSHTTPSERVLETREQUEST_SETBODY_SIG);
+
+ jbyte *jbuf = new jbyte[length];
+ for (int i = 0; i < length; i++)
+ jbuf[i] = buf[i];
+
+ jbyteArray byteArrayBuf = (env)->NewByteArray(length);
+ env->SetByteArrayRegion(byteArrayBuf, 0, length, jbuf);
+
+ //on setBodyBytes we copy buf bytes
+ env->CallVoidMethod(httpServletResponse, setBodyBytes, byteArrayBuf);
+
+ //(env)->ReleaseByteArrayElements(byteArrayBuf, jbuf, NULL);
+
+ (jvm)->DetachCurrentThread();
+ }
return APR_SUCCESS;
}
-JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_initialize(JNIEnv *env, jobject obj)
+JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_initialize(JNIEnv *env, jobject obj, jstring serverName)
{
(env)->GetJavaVM(&jvm);
modSecurityInstance = (env)->NewGlobalRef(obj); //Save the ModSecurity object for further use
@@ -334,6 +405,8 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_initialize(JNIEnv *env,
apr_pool_create(&requestsPool, NULL);
requests = apr_table_make(requestsPool, 10);
+ serverHostname = fromJString(env, serverName, requestsPool);
+
return APR_SUCCESS;
}
@@ -404,6 +477,7 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, j
modsecProcessConnection(c);
r = modsecNewRequest(c, config);
+ r->server->server_hostname = serverHostname;
jclass httpTransactionClass = env->GetObjectClass(httpTransaction);
jmethodID getHttpRequest = env->GetMethodID(httpTransactionClass, HTTPTRANSACTION_MSHTTPREQUEST_MET, HTTPTRANSACTION_MSHTTPREQUEST_SIG);
@@ -424,6 +498,7 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, j
return DONE;
}
+
jmethodID getTransactionID = env->GetMethodID(httpTransactionClass, HTTPTRANSACTION_TRANSACTIONID_MET, STRINGRETURN_SIG);
const char *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
@@ -433,15 +508,15 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, j
jobject inputStream = (env)->CallObjectMethod(httpServletRequest, getInputStream); //Request body input stream used in the read body callback
storeJavaServletContext(r, JAVASERVLET_INSTREAM, inputStream);
+ storeJavaServletContext(r, JAVASERVLET_TRANSACTION, httpTransaction);
+
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);
+ char *port_str = apr_itoa(r->pool, port);
jmethodID getPathInfo = (env)->GetMethodID(httpServletRequestClass, HTTPSERVLETREQUEST_PATHINFO_MET, STRINGRETURN_SIG);
@@ -559,9 +634,17 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onRequest(JNIEnv *env, j
#endif
c->remote_host = NULL;
-
int 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;
}
@@ -599,11 +682,7 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onResponse(JNIEnv *env,
return DONE;
}
- //jclass msOutputStreamClass = env->GetObjectClass(msOutputStream);
-
- //jmethodID getByteArrayStream = env->GetMethodID(msOutputStreamClass, MSOUTPUTSTREAM_INPUTSTREAM_MET, MSOUTPUTSTREAM_INPUTSTREAM_SIG);
- //jobject responseStream = env->CallObjectMethod(msOutputStream, getByteArrayStream);
-
+ storeJavaServletContext(r, JAVASERVLET_TRANSACTION, httpTransaction);
storeJavaServletContext(r, JAVASERVLET_OUTSTREAM, responseStream);
@@ -629,16 +708,9 @@ JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_onResponse(JNIEnv *env,
int status = modsecProcessResponse(r);
+ removeJavaServletContext(r, JAVASERVLET_OUTSTREAM);
+ removeJavaServletContext(r, JAVASERVLET_TRANSACTION);
modsecFinishRequest(r);
- // the logic here is temporary, needs clarification
- if(status != 0 && status != -1)
- {
- //reset the stream, clear the response
- jmethodID reset = (env)->GetMethodID(httpServletResponseClass, MSSERVLETRESPONSE_RESET_MET, MSSERVLETRESPONSE_RESET_SIG);
- env->CallVoidMethod(httpServletResponse, reset);
-
- }
-
return status;
}
diff --git a/java/org_modsecurity_ModSecurity.h b/java/org_modsecurity_ModSecurity.h
index ec976b47..a10e4a0b 100644
--- a/java/org_modsecurity_ModSecurity.h
+++ b/java/org_modsecurity_ModSecurity.h
@@ -16,10 +16,10 @@ extern "C" {
/*
* Class: org_modsecurity_ModSecurity
* Method: initialize
- * Signature: ()I
+ * Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_org_modsecurity_ModSecurity_initialize
- (JNIEnv *, jobject);
+ (JNIEnv *, jobject, jstring);
/*
* Class: org_modsecurity_ModSecurity