/*
* Copyright 2009-2012 Michael Dalton
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jtaint;
import java.nio.ByteBuffer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
/* This class performs Dynamic Bytecode Instrumentation (DBI) on all classes
* loaded at runtime in the JVM. This class is called by various methods in
* java.lang.ClassLoader by code inserted by jtaint.ClassLoaderAdapter.
*
* We are interested in instrumenting classes that implement various java.sql
* interfaces (Statement, Connection, RowSet), and various servlet interfaces
* (javax.servlet.ServletRequest, javax.servlet.http.HttpServletRequest,
* javax.servlet.http.Cookie, javax.servlet.http.HttpUtils).
*
* However, we cannot tell if a particular class implements these interfaces
* at load time, because the superclasses and superinterfaces may not have
* been loaded. For example, a superclass may implement one of the above
* interfaces, or this class may implement a subinterface of one of the above
* interfaces.
*
* For this reason, we instrument any method that has the same method
* descriptor as the methods we wish to perform security checks on. At runtime,
* we then perform checks on the class to ensure that our security checks
* occur only when the class actually implements one of the above interfaces.
*
* For SQL classes, java.sql is already present in rt.jar, and guaranteed to
* be available to the bootstrap loader. Thus all runtime SQL checks are
* 'instanceof' checks, ensuring that the current object is an instance of
* one of the above java.sql interfaces.
*
* For Servlet classes, javax.servlet is not present in rt.jar. The web
* application server must provide the various javax.servlet interfaces.
* Thus, we cannot directly reference javax.servlet interfaces because
* jtaint.* classes are loaded by the Java bootstrap classloader, and
* thus cannot directly reference a class loaded by the java web application's
* classloaders (if any). Furthermore, if this tool is used on a java
* program that is not a servlet, then there may be no javax.servlet package
* present at all.
*
* As a consequence, we must use reflection to dynamically probe for the
* existence of the above servlet interfaces, and then check if the current
* class implements the interface. We do this by adding a private
* final boolean to cache the result of this check for each of the above
* classes.
*/
public final class InstrumentationUtils
{
private static byte[] copyOf(byte[] b, int off, int len) {
if (off == 0 && len == b.length)
return b;
byte[] br = new byte[len];
System.arraycopy(b, off, br, 0, len);
return br;
}
public static byte[] instrument(byte[] b, int off, int len) {
try {
ClassReader cr = new ClassReader(b, off, len);
EmptyClassVisitor empty = new EmptyClassVisitor();
FilterContextAdapter ftc = new FilterContextAdapter(empty);
SqlContextAdapter sqc = new SqlContextAdapter(ftc);
ServletContextAdapter svc = new ServletContextAdapter(sqc);
XssContextAdapter xsa = new XssContextAdapter(svc);
cr.accept(xsa, ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG +
ClassReader.SKIP_FRAMES);
if (!xsa.instrumented() && !sqc.instrumented()
&& !svc.instrumented() && !ftc.instrumented())
return copyOf(b, off, len);
ClassWriter cw = new ClassWriter(cr, 0);
ClassVisitor cv = cw;
if (ftc.instrumented())
cv = new FilterAdapter(cv, ftc);
if (sqc.instrumented())
cv = new SqlAdapter(cv, sqc);
if (svc.instrumented())
cv = new ServletAdapter(cv, svc);
if (xsa.instrumented())
cv = new XssAdapter(cv, xsa);
cr.accept(cv, 0);
return cw.toByteArray();
} catch (Throwable th) {
Log.error(th);
return copyOf(b, off, len);
}
}
/* XXX We assume (as do Sun's native classes), that offset
* is equal to b.position(). and len is b.remaining(). If we're
* wrong, log it and break out. This assumption holds in
* JDK1.5/1.6, and does not occur in JDK 1.4.
*/
//[ifJava5+]
public static ByteBuffer instrument(ByteBuffer bb, int off, int len) {
try {
if (off != bb.position() || len != bb.remaining())
throw new Throwable("Unexpected ByteBuffer");
byte[] b = new byte[len];
bb.duplicate().get(b, 0, len);
byte[] instb = instrument(b, 0, len);
if (b == instb)
return bb;
ByteBuffer ret = ByteBuffer.allocateDirect(instb.length);
ret.put(instb);
ret.flip();
return ret;
} catch (Throwable th) {
Log.error(th);
return bb;
}
}
//[fiJava5+]
}