Skip to content

Commit

Permalink
Merge pull request #158 from nickl-/secure-privileged
Browse files Browse the repository at this point in the history
Secure privileged
  • Loading branch information
chibash committed Nov 14, 2017
2 parents 468b239 + ceebba3 commit c4e1949
Show file tree
Hide file tree
Showing 5 changed files with 815 additions and 188 deletions.
71 changes: 6 additions & 65 deletions src/main/javassist/ClassPool.java
Expand Up @@ -21,19 +21,16 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

import javassist.bytecode.ClassFile;
import javassist.bytecode.Descriptor;
import javassist.util.proxy.DefinePackageHelper;

/**
* A container of <code>CtClass</code> objects.
Expand Down Expand Up @@ -69,28 +66,8 @@
* @see javassist.CtClass
* @see javassist.ClassPath
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class ClassPool {
private static java.lang.reflect.Method definePackage = null;

static {
if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_9)
try {
AccessController.doPrivileged(new PrivilegedExceptionAction(){
public Object run() throws Exception{
Class cl = Class.forName("java.lang.ClassLoader");
definePackage = cl.getDeclaredMethod("definePackage",
new Class[] { String.class, String.class, String.class,
String.class, String.class, String.class,
String.class, java.net.URL.class });
return null;
}
});
}
catch (PrivilegedActionException pae) {
throw new RuntimeException("cannot initialize ClassPool",
pae.getException());
}
}

/**
* Determines the search order.
Expand Down Expand Up @@ -321,7 +298,7 @@ public void clearImportedPackages() {
* @see #importPackage(String)
* @since 3.1
*/
public Iterator getImportedPackages() {
public Iterator<String> getImportedPackages() {
return importedPackages.iterator();
}

Expand Down Expand Up @@ -1175,43 +1152,7 @@ public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain)
public void makePackage(ClassLoader loader, String name)
throws CannotCompileException
{
if (definePackage == null)
throw new CannotCompileException("give the JVM --add-opens");

Object[] args = new Object[] {
name, null, null, null, null, null, null, null };
Throwable t;
try {
makePackage2(definePackage, loader, args);
return;
}
catch (java.lang.reflect.InvocationTargetException e) {
t = e.getTargetException();
if (t == null)
t = e;
else if (t instanceof IllegalArgumentException) {
// if the package is already defined, an IllegalArgumentException
// is thrown.
return;
}
}
catch (Exception e) {
t = e;
}

throw new CannotCompileException(t);
DefinePackageHelper.definePackage(name, loader);
}

private static synchronized Object makePackage2(Method method,
ClassLoader loader, Object[] args)
throws Exception
{
method.setAccessible(true);
try {
return method.invoke(loader, args);
}
finally {
method.setAccessible(false);
}
}
}
240 changes: 158 additions & 82 deletions src/main/javassist/util/proxy/DefineClassHelper.java
Expand Up @@ -16,13 +16,11 @@

package javassist.util.proxy;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.security.ProtectionDomain;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import sun.misc.Unsafe;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;

import javassist.CannotCompileException;
import javassist.bytecode.ClassFile;
Expand All @@ -32,39 +30,153 @@
*
* @since 3.22
*/
public class DefineClassHelper {
private static java.lang.reflect.Method defineClass1 = null;
private static java.lang.reflect.Method defineClass2 = null;
private static Unsafe sunMiscUnsafe = null;

static {
if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_9)
try {
Class<?> cl = Class.forName("java.lang.ClassLoader");
defineClass1 = SecurityActions.getDeclaredMethod(
cl,
"defineClass",
new Class[] { String.class, byte[].class,
int.class, int.class });

defineClass2 = SecurityActions.getDeclaredMethod(
cl,
"defineClass",
new Class[] { String.class, byte[].class,
int.class, int.class, ProtectionDomain.class });
public class DefineClassHelper
{

private static enum SecuredPrivileged
{
JAVA_9 {
final class ReferencedUnsafe
{
private final SecurityActions.TheUnsafe sunMiscUnsafeTheUnsafe;
private final MethodHandle defineClass;

ReferencedUnsafe(SecurityActions.TheUnsafe usf, MethodHandle meth)
{
this.sunMiscUnsafeTheUnsafe = usf;
this.defineClass = meth;
}

Class<?> defineClass(String name, byte[] b, int off, int len,
ClassLoader loader, ProtectionDomain protectionDomain)
throws ClassFormatError {
if (stack.getCallerClass() != SecuredPrivileged.JAVA_9.getClass())
throw new IllegalAccessError("Access denied for caller.");
try {
return (Class<?>) defineClass.invokeWithArguments(
sunMiscUnsafeTheUnsafe.theUnsafe,
name, b, off, len, loader, protectionDomain);
} catch (Throwable e) {
if (e instanceof RuntimeException) throw (RuntimeException) e;
if (e instanceof ClassFormatError) throw (ClassFormatError) e;
throw new ClassFormatError(e.getMessage());
}
}
}
private final StackWalker stack = StackWalker
.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
private final ReferencedUnsafe sunMiscUnsafe = getReferencedUnsafe();
private final ReferencedUnsafe getReferencedUnsafe()
{
if (null != SecuredPrivileged.JAVA_9
&& stack.getCallerClass() != this.getClass())
throw new IllegalAccessError("Access denied for caller.");
try {
SecurityActions.TheUnsafe usf = SecurityActions.getSunMiscUnsafeAnonymously();
MethodHandle meth = MethodHandles.lookup()
.unreflect(usf.methods.get("defineClass").get(0));
return new ReferencedUnsafe(usf, meth);
} catch (Throwable e) {
throw new RuntimeException("cannot initialize", e);
}
}
catch (Exception e) {
throw new RuntimeException("cannot initialize");

@Override
public Class<?> defineClass(String name, byte[] b, int off, int len,
ClassLoader loader, ProtectionDomain protectionDomain)
throws ClassFormatError
{
if (stack.getCallerClass() != DefineClassHelper.class)
throw new IllegalAccessError("Access denied for caller.");
return sunMiscUnsafe.defineClass(name, b, off, len, loader,
protectionDomain);
}
else
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
sunMiscUnsafe = (sun.misc.Unsafe)theUnsafe.get(null);
},
JAVA_7 {
private final SecurityActions stack = SecurityActions.stack;
private final MethodHandle defineClass = getDefineClassMethodHandle();
private final MethodHandle getDefineClassMethodHandle()
{
if (null != SecuredPrivileged.JAVA_7
&& stack.getCallerClass() != this.getClass())
throw new IllegalAccessError("Access denied for caller.");
try {
return SecurityActions.getMethodHandle(ClassLoader.class,
"defineClass", new Class[] {
String.class, byte[].class, int.class, int.class,
ProtectionDomain.class
});
} catch (NoSuchMethodException e) {
throw new RuntimeException("cannot initialize", e);
}
}
catch (Throwable t) {}

@Override
protected Class<?> defineClass(String name, byte[] b, int off, int len,
ClassLoader loader, ProtectionDomain protectionDomain) throws ClassFormatError
{
if (stack.getCallerClass() != DefineClassHelper.class)
throw new IllegalAccessError("Access denied for caller.");
try {
return (Class<?>) defineClass.invokeWithArguments(
loader, name, b, off, len, protectionDomain);
} catch (Throwable e) {
if (e instanceof RuntimeException) throw (RuntimeException) e;
if (e instanceof ClassFormatError) throw (ClassFormatError) e;
throw new ClassFormatError(e.getMessage());
}
}
},
JAVA_OTHER {
private final Method defineClass = getDefineClassMethod();
private final SecurityActions stack = SecurityActions.stack;
private final Method getDefineClassMethod() {
if (null != SecuredPrivileged.JAVA_OTHER
&& stack.getCallerClass() != this.getClass())
throw new IllegalAccessError("Access denied for caller.");
try {
return SecurityActions.getDeclaredMethod(ClassLoader.class,
"defineClass", new Class[] {
String.class, byte[].class, int.class, int.class, ProtectionDomain.class
});
} catch (NoSuchMethodException e) {
throw new RuntimeException("cannot initialize", e);
}
}

@Override
protected Class<?> defineClass(String name, byte[] b, int off, int len,
ClassLoader loader, ProtectionDomain protectionDomain) throws ClassFormatError
{
if (stack.getCallerClass() != DefineClassHelper.class)
throw new IllegalAccessError("Access denied for caller.");
try {
SecurityActions.setAccessible(defineClass, true);
return (Class<?>) defineClass.invoke(loader, new Object[] {
name, b, off, len, protectionDomain
});
} catch (Throwable e) {
if (e instanceof ClassFormatError) throw (ClassFormatError) e;
if (e instanceof RuntimeException) throw (RuntimeException) e;
throw new ClassFormatError(e.getMessage());
}
finally {
SecurityActions.setAccessible(defineClass, false);
}
}

};

protected abstract Class<?> defineClass(String name, byte[] b, int off, int len,
ClassLoader loader, ProtectionDomain protectionDomain) throws ClassFormatError;
}

private static final SecuredPrivileged privileged = ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9
? SecuredPrivileged.JAVA_9
: ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7
? SecuredPrivileged.JAVA_7
: SecuredPrivileged.JAVA_OTHER;

/**
* Loads a class file by a given class loader.
*
Expand All @@ -84,15 +196,18 @@ public static Class<?> toClass(String className, ClassLoader loader,
ProtectionDomain domain, byte[] bcode)
throws CannotCompileException
{
if (ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9)
if (sunMiscUnsafe != null)
try {
return sunMiscUnsafe.defineClass(className, bcode, 0, bcode.length,
loader, domain);
}
catch (Throwable t2) {}

return toClass2(className, loader, domain, bcode);
try {
return privileged.defineClass(className, bcode, 0, bcode.length, loader, domain);
}
catch (RuntimeException e) {
throw e;
}
catch (ClassFormatError e) {
throw new CannotCompileException(e.getCause());
}
catch (Exception e) {
throw new CannotCompileException(e);
}
}

/**
Expand All @@ -113,44 +228,5 @@ static Class<?> toPublicClass(String className, byte[] bcode)
}
}

private static Class<?> toClass2(String cname, ClassLoader loader,
ProtectionDomain domain, byte[] bcode)
throws CannotCompileException
{
try {
Method method;
Object[] args;
if (domain == null) {
method = defineClass1;
args = new Object[] { cname, bcode, Integer.valueOf(0),
Integer.valueOf(bcode.length) };
}
else {
method = defineClass2;
args = new Object[] { cname, bcode, Integer.valueOf(0),
Integer.valueOf(bcode.length), domain };
}

return toClass3(method, loader, args);
}
catch (RuntimeException e) {
throw e;
}
catch (java.lang.reflect.InvocationTargetException e) {
throw new CannotCompileException(e.getTargetException());
}
catch (Exception e) {
throw new CannotCompileException(e);
}
}

private static synchronized
Class<?> toClass3(Method method, ClassLoader loader, Object[] args)
throws Exception
{
SecurityActions.setAccessible(method, true);
Class<?> clazz = (Class<?>)method.invoke(loader, args);
SecurityActions.setAccessible(method, false);
return clazz;
}
private DefineClassHelper() {}
}

0 comments on commit c4e1949

Please sign in to comment.