Secure privilegedtags/rel_3_23_0_ga
@@ -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); | |||
} | |||
} | |||
} |
@@ -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() {} | |||
} |
@@ -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() {} | |||
} |
@@ -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*/ } | |||
} | |||
} | |||
@@ -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();} | |||
} | |||
} |