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