Browse Source

Merge pull request #158 from nickl-/secure-privileged

Secure privileged
tags/rel_3_23_0_ga
Shigeru Chiba 6 years ago
parent
commit
c4e194994f
No account linked to committer's email address

+ 6
- 65
src/main/javassist/ClassPool.java View File

@@ -21,19 +21,16 @@ import java.io.File;
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.
@@ -69,28 +66,8 @@ import javassist.bytecode.Descriptor;
* @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.
@@ -321,7 +298,7 @@ public class ClassPool {
* @see #importPackage(String)
* @since 3.1
*/
public Iterator getImportedPackages() {
public Iterator<String> getImportedPackages() {
return importedPackages.iterator();
}

@@ -1175,43 +1152,7 @@ public class ClassPool {
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);
}
}
}

+ 158
- 82
src/main/javassist/util/proxy/DefineClassHelper.java View File

@@ -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;
@@ -32,39 +30,153 @@ import javassist.bytecode.ClassFile;
*
* @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.
*
@@ -84,15 +196,18 @@ public class DefineClassHelper {
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);
}
}

/**
@@ -113,44 +228,5 @@ public class DefineClassHelper {
}
}

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() {}
}

+ 179
- 0
src/main/javassist/util/proxy/DefinePackageHelper.java View File

@@ -0,0 +1,179 @@
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/

package javassist.util.proxy;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;

import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.bytecode.ClassFile;

/**
* Helper class for invoking {@link ClassLoader#defineClass(String,byte[],int,int)}.
*
* @since 3.22
*/
public class DefinePackageHelper
{
private static enum SecuredPrivileged
{
JAVA_9 {
// definePackage has been discontinued for JAVA 9
@Override
protected Package definePackage(ClassLoader loader, String name, String specTitle,
String specVersion, String specVendor, String implTitle, String implVersion,
String implVendor, URL sealBase) throws IllegalArgumentException
{
throw new RuntimeException("define package has been disabled for jigsaw");
}
},
JAVA_7 {
private final SecurityActions stack = SecurityActions.stack;
private final MethodHandle definePackage = getDefinePackageMethodHandle();
private MethodHandle getDefinePackageMethodHandle()
{
if (stack.getCallerClass() != this.getClass())
throw new IllegalAccessError("Access denied for caller.");
try {
return SecurityActions.getMethodHandle(ClassLoader.class,
"definePackage", new Class[] {
String.class, String.class, String.class, String.class,
String.class, String.class, String.class, URL.class
});
} catch (NoSuchMethodException e) {
throw new RuntimeException("cannot initialize", e);
}
}
@Override
protected Package definePackage(ClassLoader loader, String name, String specTitle,
String specVersion, String specVendor, String implTitle, String implVersion,
String implVendor, URL sealBase) throws IllegalArgumentException {
if (stack.getCallerClass() != DefinePackageHelper.class)
throw new IllegalAccessError("Access denied for caller.");
try {
return (Package) definePackage.invokeWithArguments(loader, name, specTitle,
specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
} catch (Throwable e) {
if (e instanceof IllegalArgumentException) throw (IllegalArgumentException) e;
if (e instanceof RuntimeException) throw (RuntimeException) e;
}
return null;
}
},
JAVA_OTHER {
private final SecurityActions stack = SecurityActions.stack;
private final Method definePackage = getDefinePackageMethod();
private Method getDefinePackageMethod()
{
if (stack.getCallerClass() != this.getClass())
throw new IllegalAccessError("Access denied for caller.");
try {
return SecurityActions.getDeclaredMethod(ClassLoader.class,
"definePackage", new Class[] {
String.class, String.class, String.class, String.class,
String.class, String.class, String.class, URL.class
});
} catch (NoSuchMethodException e) {
throw new RuntimeException("cannot initialize", e);
}
}
@Override
protected Package definePackage(ClassLoader loader, String name, String specTitle,
String specVersion, String specVendor, String implTitle, String implVersion,
String implVendor, URL sealBase) throws IllegalArgumentException
{
if (stack.getCallerClass() != DefinePackageHelper.class)
throw new IllegalAccessError("Access denied for caller.");
try {
definePackage.setAccessible(true);
return (Package) definePackage.invoke(loader, new Object[] {
name, specTitle, specVersion, specVendor, implTitle,
implVersion, implVendor, sealBase
});
} catch (Throwable e) {
if (e instanceof InvocationTargetException) {
Throwable t = ((InvocationTargetException) e).getTargetException();
if (t instanceof IllegalArgumentException)
throw (IllegalArgumentException) t;
}
if (e instanceof RuntimeException) throw (RuntimeException) e;
}
finally {
definePackage.setAccessible(false);
}
return null;
}
};

protected abstract Package definePackage(ClassLoader loader, String name, String specTitle,
String specVersion, String specVendor, String implTitle, String implVersion,
String implVendor, URL sealBase) throws IllegalArgumentException;
}

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;


/**
* Defines a new package. If the package is already defined, this method
* performs nothing.
*
* <p>You do not necessarily need to
* call this method. If this method is called, then
* <code>getPackage()</code> on the <code>Class</code> object returned
* by <code>toClass()</code> will return a non-null object.</p>
*
* <p>The jigsaw module introduced by Java 9 has broken this method.
* In Java 9 or later, the VM argument
* <code>--add-opens java.base/java.lang=ALL-UNNAMED</code>
* has to be given to the JVM so that this method can run.
* </p>
*
* @param loader the class loader passed to <code>toClass()</code> or
* the default one obtained by <code>getClassLoader()</code>.
* @param name the package name.
* @see #getClassLoader()
* @see #toClass(CtClass)
* @see CtClass#toClass() */
public static void definePackage(String className, ClassLoader loader)
throws CannotCompileException
{
try {
privileged.definePackage(loader, className,
null, null, null, null, null, null, null);
}
catch (IllegalArgumentException e) {
// if the package is already defined, an IllegalArgumentException
// is thrown.
return;
}
catch (Exception e) {
throw new CannotCompileException(e);
}
}
private DefinePackageHelper() {}
}

+ 191
- 41
src/main/javassist/util/proxy/SecurityActions.java View File

@@ -15,6 +15,8 @@
*/
package javassist.util.proxy;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -23,46 +25,99 @@ import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class SecurityActions {
static Method[] getDeclaredMethods(final Class clazz) {
import javassist.bytecode.ClassFile;

class SecurityActions extends SecurityManager
{
public static final SecurityActions stack = new SecurityActions();
/**
* Since Java 9 abruptly removed <code>Reflection.getCallerClass()</code>
* in favour of <code>StackWalker</code> we are left having to find a
* solution for the older versions without upsetting the new compiler.
*
* The member scoped function <code>getClassContext()</code>
* available as a <code>SecurityManager</code> sibling remains
* functional across all versions, for now.
*
* @return represents the declaring class of the method that invoked
* the method that called this or idx 2 on the stack trace.
* @since 3.23 */
public Class<?> getCallerClass()
{
return getClassContext()[2];
}


static Method[] getDeclaredMethods(final Class<?> clazz)
{
if (System.getSecurityManager() == null)
return clazz.getDeclaredMethods();
else {
return (Method[]) AccessController
.doPrivileged(new PrivilegedAction() {
public Object run() {
return clazz.getDeclaredMethods();
}
});
return AccessController.doPrivileged(
new PrivilegedAction<Method[]>() {
public Method[] run() {
return clazz.getDeclaredMethods();
}
});
}
}

static Constructor[] getDeclaredConstructors(final Class clazz) {
static Constructor<?>[] getDeclaredConstructors(final Class<?> clazz)
{
if (System.getSecurityManager() == null)
return clazz.getDeclaredConstructors();
else {
return (Constructor[]) AccessController
.doPrivileged(new PrivilegedAction() {
public Object run() {
return clazz.getDeclaredConstructors();
}
});
return AccessController.doPrivileged(
new PrivilegedAction<Constructor<?>[]>() {
public Constructor<?>[] run() {
return clazz.getDeclaredConstructors();
}
});
}
}

static Method getDeclaredMethod(final Class clazz, final String name,
final Class[] types) throws NoSuchMethodException {
static MethodHandle getMethodHandle(final Class<?> clazz, final
String name, final Class<?>[] params) throws NoSuchMethodException
{
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<MethodHandle>() {
public MethodHandle run() throws IllegalAccessException,
NoSuchMethodException, SecurityException {
Method rmet = clazz.getDeclaredMethod(name, params);
rmet.setAccessible(true);
MethodHandle meth = MethodHandles.lookup().unreflect(rmet);
rmet.setAccessible(false);
return meth;
}
});
}
catch (PrivilegedActionException e) {
if (e.getCause() instanceof NoSuchMethodException)
throw (NoSuchMethodException) e.getCause();
throw new RuntimeException(e.getCause());
}
}

static Method getDeclaredMethod(final Class<?> clazz, final String name,
final Class<?>[] types) throws NoSuchMethodException
{
if (System.getSecurityManager() == null)
return clazz.getDeclaredMethod(name, types);
else {
try {
return (Method) AccessController
.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
return clazz.getDeclaredMethod(name, types);
}
});
return AccessController.doPrivileged(
new PrivilegedExceptionAction<Method>() {
public Method run() throws Exception {
return clazz.getDeclaredMethod(name, types);
}
});
}
catch (PrivilegedActionException e) {
if (e.getCause() instanceof NoSuchMethodException)
@@ -73,20 +128,20 @@ class SecurityActions {
}
}

static Constructor getDeclaredConstructor(final Class clazz,
final Class[] types)
static Constructor<?> getDeclaredConstructor(final Class<?> clazz,
final Class<?>[] types)
throws NoSuchMethodException
{
if (System.getSecurityManager() == null)
return clazz.getDeclaredConstructor(types);
else {
try {
return (Constructor) AccessController
.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
return clazz.getDeclaredConstructor(types);
}
});
return AccessController.doPrivileged(
new PrivilegedExceptionAction<Constructor<?>>() {
public Constructor<?> run() throws Exception {
return clazz.getDeclaredConstructor(types);
}
});
}
catch (PrivilegedActionException e) {
if (e.getCause() instanceof NoSuchMethodException)
@@ -98,12 +153,13 @@ class SecurityActions {
}

static void setAccessible(final AccessibleObject ao,
final boolean accessible) {
final boolean accessible)
{
if (System.getSecurityManager() == null)
ao.setAccessible(accessible);
else {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ao.setAccessible(accessible);
return null;
}
@@ -118,19 +174,113 @@ class SecurityActions {
fld.set(target, value);
else {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
fld.set(target, value);
return null;
}
});
AccessController.doPrivileged(
new PrivilegedExceptionAction<Void>() {
public Void run() throws Exception {
fld.set(target, value);
return null;
}
});
}
catch (PrivilegedActionException e) {
if (e.getCause() instanceof NoSuchMethodException)
throw (IllegalAccessException) e.getCause();

throw new RuntimeException(e.getCause());
}
}
}

static TheUnsafe getSunMiscUnsafeAnonymously() throws ClassNotFoundException
{
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<TheUnsafe>() { public TheUnsafe run() throws
ClassNotFoundException, NoSuchFieldException, SecurityException,
IllegalArgumentException, IllegalAccessException {
Class<?> unsafe = Class.forName("sun.misc.Unsafe");
Field theUnsafe = unsafe.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
TheUnsafe usf = stack.new TheUnsafe(unsafe, theUnsafe.get(null));
theUnsafe.setAccessible(false);
disableWarning(usf);
return usf;
}
});
}
catch (PrivilegedActionException e) {
if (e.getCause() instanceof ClassNotFoundException)
throw (ClassNotFoundException) e.getCause();
if (e.getCause() instanceof NoSuchFieldException)
throw new ClassNotFoundException("No such instance.", e.getCause());
if (e.getCause() instanceof IllegalAccessException
|| e.getCause() instanceof IllegalAccessException
|| e.getCause() instanceof SecurityException)
throw new ClassNotFoundException("Security denied access.", e.getCause());
throw new RuntimeException(e.getCause());
}
}
/**
* _The_ Notorious sun.misc.Unsafe in all its glory, but anonymous
* so as not to attract unwanted attention. Kept in two separate
* parts it manages to avoid detection from linker/compiler/general
* complainers and those. This functionality will vanish from the
* JDK soon but in the meantime it shouldn't be an obstacle.
*
* All exposed methods are cached in a dictionary with overloaded
* methods collected under their corresponding keys. Currently the
* implementation assumes there is only one, if you need find a
* need there will have to be a compare.
* @since 3.23 */
class TheUnsafe
{
final Class<?> unsafe;
final Object theUnsafe;
final Map<String, List<Method>> methods =
new HashMap<String, List<Method>>();

TheUnsafe(Class<?> c, Object o)
{
this.unsafe = c;
this.theUnsafe = o;
for (Method m: unsafe.getDeclaredMethods()) {
if (!methods.containsKey(m.getName())) {
methods.put(m.getName(), Collections.singletonList(m));
continue;
}
if (methods.get(m.getName()).size() == 1)
methods.put(m.getName(),
new ArrayList<Method>(methods.get(m.getName())));
methods.get(m.getName()).add(m);
}
}

private Method getM(String name, Object[] o)
{
return methods.get(name).get(0);
}

public Object call(String name, Object... args)
{
try {
return getM(name, args).invoke(theUnsafe, args);
} catch (Throwable t) {t.printStackTrace();}
return null;
}
}
/**
* Java 9 now complains about every privileged action regardless.
* Displaying warnings of "illegal usage" and then instructing users
* to go hassle the maintainers in order to have it fixed.
* Making it hush for now, see all fixed.
* @param tu theUnsafe that'll fix it */
static void disableWarning(TheUnsafe tu) {
try {
if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_9)
return;
Class<?> cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
Field logger = cls.getDeclaredField("logger");
tu.call("putObjectVolatile", cls, tu.call("staticFieldOffset", logger), null);
} catch (Exception e) { /*swallow*/ }
}
}


+ 281
- 0
src/test/test/javassist/proxy/TestSecuredPrivileged.java View File

@@ -0,0 +1,281 @@
package test.javassist.proxy;
import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.both;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.startsWith;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import java.util.Arrays;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.util.proxy.DefineClassHelper;

public class TestSecuredPrivileged {

public TestSecuredPrivileged() {
}

@Rule
public ExpectedException thrown = ExpectedException.none();
/**
* Test proves that you cannot even access members with
* private static and final modifiers. */
@Test
public void testDefinedHelperPrivilegedFieldVisibility() {
try {
Field privi = DefineClassHelper.class.getDeclaredField("privileged");
assertTrue(Modifier.isStatic(privi.getModifiers()));
thrown.expectCause(instanceOf(IllegalAccessException.class));
thrown.expectMessage(both(stringContainsInOrder(Arrays.asList("cannot access a member")))
.and(stringContainsInOrder(Arrays.asList("with modifiers \"private static final".split("", 1)))));
privi.get(null);
} catch(Throwable t) {
throw new RuntimeException(t);
}
}
/**
* Test proves that the default enum constant is a class and specifically
* auto selected for Java 9. */
@Test
public void testDefinedHelperPrivilegedField() {
try {
Field privi = DefineClassHelper.class.getDeclaredField("privileged");
assertTrue(Modifier.isStatic(privi.getModifiers()));
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor();
con.setAccessible(true);
DefineClassHelper inst = con.newInstance();
assertThat(inst, instanceOf(DefineClassHelper.class));
privi.setAccessible(true);
Object p = privi.get(inst);
assertThat(""+p, equalTo("JAVA_9"));
assertThat(p.getClass().getName(), endsWith("SecuredPrivileged$1"));
} catch(Throwable t) {
throw new RuntimeException(t);
}
}
/**
* Test proves that caller class security is enforced and works
* as expected. */
@Test
public void testDefinedHelperPrivilegedFieldMethodAccessDenied() {
try {
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor();
con.setAccessible(true);
DefineClassHelper inst = con.newInstance();
Field privi = DefineClassHelper.class.getDeclaredField("privileged");
privi.setAccessible(true);
Object priviInst = privi.get(inst);
Method defineClass = priviInst.getClass().getDeclaredMethod(
"defineClass", new Class[] {
String.class, byte[].class, int.class, int.class,
ClassLoader.class, ProtectionDomain.class
});
assertThat(defineClass, notNullValue());
defineClass.setAccessible(true);
assertThat(defineClass.getName(), equalTo("defineClass"));
assertTrue(defineClass.canAccess(priviInst));
ClassPool cp = ClassPool.getDefault();
CtClass c = cp.makeClass("a.b.C");
byte[] bc = c.toBytecode();

thrown.expectCause(instanceOf(IllegalAccessError.class));
thrown.expectMessage(equalTo("java.lang.IllegalAccessError: Access denied for caller."));

@SuppressWarnings("unused")
Object res = defineClass.invoke(priviInst, new Object[] {
c.getName(), bc, 0, bc.length, new ClassLoader() {},
ClassLoader.class.getProtectionDomain()
});
} catch(InvocationTargetException t) {
throw new RuntimeException(t.getTargetException());
} catch(Throwable t) { throw new RuntimeException(t); }
}
/**
* Test proves that we do have 3 enum constants in the private static
* inner class. */
@Test
public void testDefinedHelperEnumClass() {
try {
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor();
con.setAccessible(true);
assertThat(DefineClassHelper.class.getDeclaredClasses(), arrayWithSize(1));
Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0];
assertTrue(secPriv.isEnum());
assertThat(secPriv.getEnumConstants(), arrayWithSize(3));
assertThat(""+secPriv.getEnumConstants()[0], equalTo("JAVA_9"));
assertThat(""+secPriv.getEnumConstants()[1], equalTo("JAVA_7"));
assertThat(""+secPriv.getEnumConstants()[2], equalTo("JAVA_OTHER"));

} catch (Throwable t) {t.printStackTrace();}

}
/**
* Test proves that you cannot modify private static final reference even
* with setAccessible(true). */
@Test
public void testDefinedHelperCannotSetPrivileged() {
try {
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor();
con.setAccessible(true);
DefineClassHelper inst = con.newInstance();
Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0];
Object J7 = secPriv.getEnumConstants()[1];
Field privi = DefineClassHelper.class.getDeclaredField("privileged");
privi.setAccessible(true);
thrown.expectCause(instanceOf(IllegalAccessException.class));
thrown.expectMessage(startsWith("java.lang.IllegalAccessException: Can not set static final"));
privi.set(inst, J7);

} catch (Throwable t) {throw new RuntimeException(t);}

}
/**
* Test proves that you can achieve the impossible and modify private
* static final class reference without an instance. Now we can Mock
* test JDK 6 to 8 functionality */
@Test
public void testDefinedHelperSetPrivilegedToJava7() {
try {
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor();
con.setAccessible(true);
DefineClassHelper inst = con.newInstance();
Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0];
Object J9 = secPriv.getEnumConstants()[0];
Object J7 = secPriv.getEnumConstants()[1];
Field privi = DefineClassHelper.class.getDeclaredField("privileged");
privi.setAccessible(true);
Object privInst = privi.get(inst);
Field unsf = privInst.getClass().getDeclaredField("sunMiscUnsafe");
unsf.setAccessible(true);
Object refu = unsf.get(privInst);
Field tuf = refu.getClass().getDeclaredField("sunMiscUnsafeTheUnsafe");
tuf.setAccessible(true);
Object tu = tuf.get(refu);
Method tu_call = tu.getClass().getMethod("call", new Class<?>[] {String.class, Object[].class});
tu_call.setAccessible(true);
long offset = (Long) tu_call.invoke(tu, new Object[] {"staticFieldOffset", new Object[] {privi}});
tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J7}});

Object p = privi.get(inst);
assertThat(""+p, equalTo("JAVA_7"));
assertThat(p.getClass().getName(), endsWith("SecuredPrivileged$2"));

tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J9}});

} catch (Throwable t) {t.printStackTrace();}

}
/**
* Test proves that Java 7+ MethodHandle defineClass (or DefineClassHelper.toClass)
* works as expected. */
@Test
public void testDefinedHelperJava7ToClass() {
try {
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor();
con.setAccessible(true);
DefineClassHelper inst = con.newInstance();
Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0];
Object J9 = secPriv.getEnumConstants()[0];
Object J7 = secPriv.getEnumConstants()[1];
Field privi = DefineClassHelper.class.getDeclaredField("privileged");
privi.setAccessible(true);
Object privInst = privi.get(inst);
Field unsf = privInst.getClass().getDeclaredField("sunMiscUnsafe");
unsf.setAccessible(true);
Object refu = unsf.get(privInst);
Field tuf = refu.getClass().getDeclaredField("sunMiscUnsafeTheUnsafe");
tuf.setAccessible(true);
Object tu = tuf.get(refu);
Method tu_call = tu.getClass().getMethod("call", new Class<?>[] {String.class, Object[].class});
tu_call.setAccessible(true);
long offset = (Long) tu_call.invoke(tu, new Object[] {"staticFieldOffset", new Object[] {privi}});
tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J7}});

ClassPool cp = ClassPool.getDefault();
CtClass c = cp.makeClass("a.b.J7");
byte[] bc = c.toBytecode();
Class<?> bcCls = DefineClassHelper.toClass("a.b.J7", new ClassLoader() {}, null, bc);
assertThat(bcCls.getName(), equalTo("a.b.J7"));
assertThat(bcCls.getDeclaredConstructor().newInstance(),
not(equalTo(bcCls.getDeclaredConstructor().newInstance())));

tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J9}});
} catch (Throwable t) {t.printStackTrace();}

}
/**
* Test proves that Java 6 reflection method defineClass (or DefineClassHelper.toClass)
* works as expected. */
@Test
public void testDefinedHelperJavaOtherToClass() {
try {
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor();
con.setAccessible(true);
DefineClassHelper inst = con.newInstance();
Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0];
Object J9 = secPriv.getEnumConstants()[0];
Object JO = secPriv.getEnumConstants()[2];
Field privi = DefineClassHelper.class.getDeclaredField("privileged");
privi.setAccessible(true);
Object privInst = privi.get(inst);
Field unsf = privInst.getClass().getDeclaredField("sunMiscUnsafe");
unsf.setAccessible(true);
Object refu = unsf.get(privInst);
Field tuf = refu.getClass().getDeclaredField("sunMiscUnsafeTheUnsafe");
tuf.setAccessible(true);
Object tu = tuf.get(refu);
Method tu_call = tu.getClass().getMethod("call", new Class<?>[] {String.class, Object[].class});
tu_call.setAccessible(true);
long offset = (Long) tu_call.invoke(tu, new Object[] {"staticFieldOffset", new Object[] {privi}});
tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, JO}});

ClassPool cp = ClassPool.getDefault();
CtClass c = cp.makeClass("a.b.JO");
byte[] bc = c.toBytecode();
Class<?> bcCls = DefineClassHelper.toClass("a.b.JO", new ClassLoader() {}, null, bc);
assertThat(bcCls.getName(), equalTo("a.b.JO"));
assertThat(bcCls.getDeclaredConstructor().newInstance(),
not(equalTo(bcCls.getDeclaredConstructor().newInstance())));

tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J9}});
} catch (Throwable t) {t.printStackTrace();}

}
/**
* Test proves that default Java 9 defineClass (or DefineClassHelper.toClass)
* works as expected. */
@Test
public void testDefinedHelperDefaultToClass() {
try {
ClassPool cp = ClassPool.getDefault();
CtClass c = cp.makeClass("a.b.D");
byte[] bc = c.toBytecode();
Class<?> bcCls = DefineClassHelper.toClass("a.b.D", new ClassLoader() {}, null, bc);
assertThat(bcCls.getName(), equalTo("a.b.D"));
assertThat(bcCls.getDeclaredConstructor().newInstance(),
not(equalTo(bcCls.getDeclaredConstructor().newInstance())));
} catch (Throwable t) {t.printStackTrace();}

}
}

Loading…
Cancel
Save