/* * JBoss, Home of Professional Open Source. * Copyright 2013, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.capedwarf.bytecode.blacklist; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import org.jboss.capedwarf.shared.blacklist.BlackList; import sun.reflect.Reflection; /** * Restrictions * * Based on Google App Engine's Runtime class. * * @author rudominer@google.com * @author ludo@google.com * @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a> */ public class Restrictions { public static void reject(String className) { throw new NoClassDefFoundError(className + " is a restricted class. Please see the Google App Engine developer's guide for more details."); } @SuppressWarnings("unused") public static void checkRestricted(boolean violationIsError, String classStr, String callingClassStr, String callingClassCodeSource) { if (System.getProperty("appengine.disableRestrictedCheck") == null) { // TODO } } public static ClassLoader checkParentClassLoader(ClassLoader loader) { ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); return loader != null && loader != systemLoader ? loader : Restrictions.class.getClassLoader(); } private static boolean isBlackListed(Class<?> klass) { return BlackList.getBlackList().contains(klass.getName()); } private static Class<?> verifyWhiteListed(Member m) throws IllegalAccessException { Class<?> klass = m.getDeclaringClass(); if (!isWhiteListed(klass, m)) { throw new IllegalAccessException("Reflection is not allowed on " + m); } return klass; } private static void verifyReadable(Class<?> caller, Field f, Object target) throws IllegalAccessException { checkAccess(f, target, caller); verifyWhiteListed(f); } private static void verifyWritable(Class<?> caller, Field f, Object target) throws IllegalAccessException { checkAccess(f, target, caller); Class<?> klass = verifyWhiteListed(f); if (getClassLoaderPrivileged(klass) == null) { if (!Modifier.isPublic(f.getModifiers())) { throw new IllegalAccessException("Private fields can not be set on JRE classes."); } } } private static ClassLoader getClassLoaderPrivileged(final Class<?> klass) { return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { return klass.getClassLoader(); } }); } public static Object invoke(Method method, Object target, Object[] args) throws InvocationTargetException, IllegalAccessException { checkAccess(method, target, getCallerClassPrivileged(3)); verifyWhiteListed(method); AccessibilityModifier am = new AccessibilityModifier(method); try { return method.invoke(target, args); } finally { am.restore(); } } private static Object newInstance_(Class<?> callerClass, Constructor<?> cons, Object[] args) throws InstantiationException, IllegalAccessException, InvocationTargetException { checkAccess(cons, null, callerClass); verifyWhiteListed(cons); AccessibilityModifier am = new AccessibilityModifier(cons); try { return cons.newInstance(args); } finally { am.restore(); } } public static Object newInstance(Constructor<?> cons, Object[] args) throws InstantiationException, IllegalAccessException, InvocationTargetException { return newInstance_(getImmediateCallerClassPrivileged(), cons, args); } public static Object newInstance(final Class<?> klass) throws InstantiationException, IllegalAccessException { try { Constructor<?> cons = AccessController.doPrivileged( new PrivilegedExceptionAction<Constructor<?>>() { @Override public Constructor<?> run() throws NoSuchMethodException { return klass.getDeclaredConstructor(); } }); return newInstance_(getImmediateCallerClassPrivileged(), cons, new Object[0]); } catch (PrivilegedActionException e) { Throwable t = e.getCause(); if (t instanceof NoSuchMethodException) { throw new InstantiationException(t.getMessage()); } throw new RuntimeException(t); } catch (InvocationTargetException e) { throwAsUnchecked(e.getCause()); throw new InstantiationException((e.getCause().getMessage())); } } public static Object get(final Field f, final Object obj) throws IllegalAccessException, IllegalArgumentException { return verifyAndRun(f, obj, Op.Get, new Action<Object>() { @Override public Object run(Field f, Object target) throws IllegalAccessException { return f.get(target); } }); } public static boolean getBoolean(final Field f, final Object obj) throws IllegalAccessException, IllegalArgumentException { return verifyAndRun(f, obj, Op.Get, new Action<Boolean>() { @Override public Boolean run(Field f, Object target) throws IllegalAccessException { return f.getBoolean(target); } }); } public static byte getByte(final Field f, final Object obj) throws IllegalAccessException, IllegalArgumentException { return verifyAndRun(f, obj, Op.Get, new Action<Byte>() { @Override public Byte run(Field f, Object target) throws IllegalAccessException { return f.getByte(target); } }); } public static char getChar(final Field f, final Object obj) throws IllegalAccessException, IllegalArgumentException { return verifyAndRun(f, obj, Op.Get, new Action<Character>() { @Override public Character run(Field f, Object target) throws IllegalAccessException { return f.getChar(target); } }); } public static double getDouble(final Field f, final Object obj) throws IllegalAccessException, IllegalArgumentException { return verifyAndRun(f, obj, Op.Get, new Action<Double>() { @Override public Double run(Field f, Object target) throws IllegalAccessException { return f.getDouble(target); } }); } public static float getFloat(final Field f, final Object obj) throws IllegalAccessException, IllegalArgumentException { return verifyAndRun(f, obj, Op.Get, new Action<Float>() { @Override public Float run(Field f, Object target) throws IllegalAccessException { return f.getFloat(target); } }); } public static int getInt(final Field f, final Object obj) throws IllegalAccessException, IllegalArgumentException { return verifyAndRun(f, obj, Op.Get, new Action<Integer>() { @Override public Integer run(Field f, Object target) throws IllegalAccessException { return f.getInt(target); } }); } public static long getLong(final Field f, final Object obj) throws IllegalAccessException, IllegalArgumentException { return verifyAndRun(f, obj, Op.Get, new Action<Long>() { @Override public Long run(Field f, Object target) throws IllegalAccessException { return f.getLong(target); } }); } public static short getShort(final Field f, final Object obj) throws IllegalAccessException, IllegalArgumentException { return verifyAndRun(f, obj, Op.Get, new Action<Short>() { @Override public Short run(Field f, Object target) throws IllegalAccessException { return f.getShort(target); } }); } public static void set(Field f, Object obj, final Object value) throws IllegalAccessException, IllegalArgumentException { verifyAndRun(f, obj, Op.Set, new Action<Object>() { @Override public Object run(Field f, Object target) throws IllegalAccessException { f.set(target, value); return null; } }); } public static void setBoolean(Field f, Object obj, final boolean value) throws IllegalAccessException, IllegalArgumentException { verifyAndRun(f, obj, Op.Set, new Action<Object>() { @Override public Object run(Field f, Object target) throws IllegalAccessException { f.setBoolean(target, value); return null; } }); } public static void setByte(Field f, Object obj, final byte value) throws IllegalAccessException, IllegalArgumentException { verifyAndRun(f, obj, Op.Set, new Action<Object>() { @Override public Object run(Field f, Object target) throws IllegalAccessException { f.setByte(target, value); return null; } }); } public static void setChar(Field f, Object obj, final char value) throws IllegalAccessException, IllegalArgumentException { verifyAndRun(f, obj, Op.Set, new Action<Object>() { @Override public Object run(Field f, Object target) throws IllegalAccessException { f.setChar(target, value); return null; } }); } public static void setDouble(Field f, Object obj, final double value) throws IllegalAccessException, IllegalArgumentException { verifyAndRun(f, obj, Op.Set, new Action<Object>() { @Override public Object run(Field f, Object target) throws IllegalAccessException { f.setDouble(target, value); return null; } }); } public static void setFloat(Field f, Object obj, final float value) throws IllegalAccessException, IllegalArgumentException { verifyAndRun(f, obj, Op.Set, new Action<Object>() { @Override public Object run(Field f, Object target) throws IllegalAccessException { f.setFloat(target, value); return null; } }); } public static void setInt(Field f, Object obj, final int value) throws IllegalAccessException, IllegalArgumentException { verifyAndRun(f, obj, Op.Set, new Action<Object>() { @Override public Object run(Field f, Object target) throws IllegalAccessException { f.setInt(target, value); return null; } }); } public static void setLong(Field f, Object obj, final long value) throws IllegalAccessException, IllegalArgumentException { verifyAndRun(f, obj, Op.Set, new Action<Object>() { @Override public Object run(Field f, Object target) throws IllegalAccessException { f.setLong(target, value); return null; } }); } public static void setShort(Field f, Object obj, final short value) throws IllegalAccessException, IllegalArgumentException { verifyAndRun(f, obj, Op.Set, new Action<Object>() { @Override public Object run(Field f, Object target) throws IllegalAccessException { f.setShort(target, value); return null; } }); } enum Op { Get, Set } private interface Action<T> { public T run(Field f, Object target) throws IllegalAccessException; } private static <T> T verifyAndRun(Field f, Object target, Op op, Action<T> action) throws IllegalAccessException { Class<?> userCaller = getCallerClassPrivileged(3); if (op == Op.Get) { verifyReadable(userCaller, f, target); } else { verifyWritable(userCaller, f, target); } AccessibilityModifier am = new AccessibilityModifier(f); try { return action.run(f, target); } finally { am.restore(); } } private static boolean isWhiteListed(Class<?> klass, Member member) { if (klass == null) { return false; } if (isWhiteListed_(klass, member)) { return true; } if (member instanceof Constructor) { return false; } Class<?>[] interfaces = klass.getInterfaces(); for (Class<?> i : interfaces) { if (isWhiteListed(i, member)) { return true; } } Class<?> parentClass = klass.getSuperclass(); return isWhiteListed(parentClass, member); } private static boolean isWhiteListed_(Class<?> klass, Member member) { return !isBlackListed(klass) && (klass == member.getDeclaringClass() || hasMember(klass, member)); } private static boolean hasMember(Class<?> klass, Member member) { if (member instanceof Method) { Method m = (Method) member; try { klass.getDeclaredMethod(m.getName(), m.getParameterTypes()); return true; } catch (NoSuchMethodException e) { return false; } } if (member instanceof Constructor) { Constructor<?> constructor = (Constructor<?>) member; try { klass.getDeclaredConstructor(constructor.getParameterTypes()); return true; } catch (NoSuchMethodException e) { return false; } } Field field = (Field) member; try { klass.getDeclaredField(field.getName()); return true; } catch (NoSuchFieldException e) { return false; } } private static Class<?> getImmediateCallerClassPrivileged() { return getCallerClassPrivileged(3); } private static Class<?> getCallerClassPrivileged(final int depth) { return AccessController.doPrivileged(new PrivilegedAction<Class<?>>() { @Override public Class<?> run() { return Reflection.getCallerClass(depth + 4); } }); } private static <T extends AccessibleObject & Member> void checkAccess(final T m, final Object target, Class<?> caller) throws IllegalAccessException { if (m.isAccessible()) { return; } checkAccess(caller, m.getDeclaringClass(), target, m.getModifiers()); } private static void checkAccess(final Class<?> caller, final Class<?> member, final Object target, final int modifiers) throws IllegalAccessException { try { AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { @Override public Object run() throws IllegalAccessException { Reflection.ensureMemberAccess(caller, member, target, modifiers); return null; } }); } catch (PrivilegedActionException e) { Throwable t = e.getCause(); if (t instanceof IllegalAccessException) { throw (IllegalAccessException) t; } throw new RuntimeException(t); } } public static void throwAsUnchecked(Throwable t) { Restrictions.<RuntimeException>throwException_(t); } @SuppressWarnings("unchecked") private static <T extends Throwable> void throwException_(Throwable t) throws T { throw (T) t; } static class AccessibilityModifier { private final AccessibleObject obj; private final boolean originalAccessibility; AccessibilityModifier(AccessibleObject obj) { this.obj = obj; originalAccessibility = obj.isAccessible(); setAccessible_(true); } public void restore() { setAccessible_(originalAccessibility); } private void setAccessible_(final boolean flag) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { obj.setAccessible(flag); return null; } }); } } }