1 /*
   2  * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 package sun.reflect.misc;
  28 
  29 import java.lang.reflect.Member;
  30 import java.lang.reflect.Method;
  31 import java.lang.reflect.Modifier;
  32 import java.lang.reflect.Proxy;
  33 import jdk.internal.reflect.Reflection;
  34 import sun.security.util.SecurityConstants;
  35 
  36 public final class ReflectUtil {
  37 
  38     private ReflectUtil() {
  39     }
  40 
  41     public static Class<?> forName(String name)
  42         throws ClassNotFoundException {
  43         checkPackageAccess(name);
  44         return Class.forName(name);
  45     }
  46 
  47     /**
  48      * Ensures that access to a method or field is granted and throws
  49      * IllegalAccessException if not. This method is not suitable for checking
  50      * access to constructors.
  51      *
  52      * @param currentClass the class performing the access
  53      * @param memberClass the declaring class of the member being accessed
  54      * @param target the target object if accessing instance field or method;
  55      *               or null if accessing static field or method or if target
  56      *               object access rights will be checked later
  57      * @param modifiers the member's access modifiers
  58      * @throws IllegalAccessException if access to member is denied
  59      * @implNote Delegates directly to
  60      *           {@link Reflection#ensureMemberAccess(Class, Class, Class, int)}
  61      *           which should be used instead.
  62      */
  63     public static void ensureMemberAccess(Class<?> currentClass,
  64                                           Class<?> memberClass,
  65                                           Object target,
  66                                           int modifiers)
  67         throws IllegalAccessException
  68     {
  69         Reflection.ensureMemberAccess(currentClass,
  70                                       memberClass,
  71                                       target == null ? null : target.getClass(),
  72                                       modifiers);
  73     }
  74 
  75     /**
  76      * Does a conservative approximation of member access check. Use this if
  77      * you don't have an actual 'userland' caller Class/ClassLoader available.
  78      * This might be more restrictive than a precise member access check where
  79      * you have a caller, but should never allow a member access that is
  80      * forbidden.
  81      *
  82      * @param m the {@code Member} about to be accessed
  83      */
  84     public static void conservativeCheckMemberAccess(Member m) throws SecurityException{
  85         final SecurityManager sm = System.getSecurityManager();
  86         if (sm == null)
  87             return;
  88 
  89         // Check for package access on the declaring class.
  90         //
  91         // In addition, unless the member and the declaring class are both
  92         // public check for access declared member permissions.
  93         //
  94         // This is done regardless of ClassLoader relations between the {@code
  95         // Member m} and any potential caller.
  96 
  97         final Class<?> declaringClass = m.getDeclaringClass();
  98 
  99         privateCheckPackageAccess(sm, declaringClass);
 100 
 101         if (Modifier.isPublic(m.getModifiers()) &&
 102                 Modifier.isPublic(declaringClass.getModifiers()))
 103             return;
 104 
 105         // Check for declared member access.
 106         sm.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
 107     }
 108 
 109     /**
 110      * Checks package access on the given class.
 111      *
 112      * If it is a {@link Proxy#isProxyClass(java.lang.Class)} that implements
 113      * a non-public interface (i.e. may be in a non-restricted package),
 114      * also check the package access on the proxy interfaces.
 115      */
 116     public static void checkPackageAccess(Class<?> clazz) {
 117         SecurityManager s = System.getSecurityManager();
 118         if (s != null) {
 119             privateCheckPackageAccess(s, clazz);
 120         }
 121     }
 122 
 123     /**
 124      * NOTE: should only be called if a SecurityManager is installed
 125      */
 126     private static void privateCheckPackageAccess(SecurityManager s, Class<?> clazz) {
 127         while (clazz.isArray()) {
 128             clazz = clazz.getComponentType();
 129         }
 130 
 131         String pkg = clazz.getPackageName();
 132         if (pkg != null && !pkg.isEmpty()) {
 133             s.checkPackageAccess(pkg);
 134         }
 135 
 136         if (isNonPublicProxyClass(clazz)) {
 137             privateCheckProxyPackageAccess(s, clazz);
 138         }
 139     }
 140 
 141     /**
 142      * Checks package access on the given classname.
 143      * This method is typically called when the Class instance is not
 144      * available and the caller attempts to load a class on behalf
 145      * the true caller (application).
 146      */
 147     public static void checkPackageAccess(String name) {
 148         SecurityManager s = System.getSecurityManager();
 149         if (s != null) {
 150             String cname = name.replace('/', '.');
 151             if (cname.startsWith("[")) {
 152                 int b = cname.lastIndexOf('[') + 2;
 153                 if (b > 1 && b < cname.length()) {
 154                     cname = cname.substring(b);
 155                 }
 156             }
 157             int i = cname.lastIndexOf('.');
 158             if (i != -1) {
 159                 s.checkPackageAccess(cname.substring(0, i));
 160             }
 161         }
 162     }
 163 
 164     public static boolean isPackageAccessible(Class<?> clazz) {
 165         try {
 166             checkPackageAccess(clazz);
 167         } catch (SecurityException e) {
 168             return false;
 169         }
 170         return true;
 171     }
 172 
 173     // Returns true if p is an ancestor of cl i.e. class loader 'p' can
 174     // be found in the cl's delegation chain
 175     private static boolean isAncestor(ClassLoader p, ClassLoader cl) {
 176         ClassLoader acl = cl;
 177         do {
 178             acl = acl.getParent();
 179             if (p == acl) {
 180                 return true;
 181             }
 182         } while (acl != null);
 183         return false;
 184     }
 185 
 186     /**
 187      * Returns true if package access check is needed for reflective
 188      * access from a class loader 'from' to classes or members in
 189      * a class defined by class loader 'to'.  This method returns true
 190      * if 'from' is not the same as or an ancestor of 'to'.  All code
 191      * in a system domain are granted with all permission and so this
 192      * method returns false if 'from' class loader is a class loader
 193      * loading system classes.  On the other hand, if a class loader
 194      * attempts to access system domain classes, it requires package
 195      * access check and this method will return true.
 196      */
 197     public static boolean needsPackageAccessCheck(ClassLoader from, ClassLoader to) {
 198         if (from == null || from == to)
 199             return false;
 200 
 201         if (to == null)
 202             return true;
 203 
 204         return !isAncestor(from, to);
 205     }
 206 
 207     /**
 208      * Check package access on the proxy interfaces that the given proxy class
 209      * implements.
 210      *
 211      * @param clazz Proxy class object
 212      */
 213     public static void checkProxyPackageAccess(Class<?> clazz) {
 214         SecurityManager s = System.getSecurityManager();
 215         if (s != null) {
 216             privateCheckProxyPackageAccess(s, clazz);
 217         }
 218     }
 219 
 220     /**
 221      * NOTE: should only be called if a SecurityManager is installed
 222      */
 223     private static void privateCheckProxyPackageAccess(SecurityManager s, Class<?> clazz) {
 224         // check proxy interfaces if the given class is a proxy class
 225         if (Proxy.isProxyClass(clazz)) {
 226             for (Class<?> intf : clazz.getInterfaces()) {
 227                 privateCheckPackageAccess(s, intf);
 228             }
 229         }
 230     }
 231     /**
 232      * Access check on the interfaces that a proxy class implements and throw
 233      * {@code SecurityException} if it accesses a restricted package from
 234      * the caller's class loader.
 235      *
 236      * @param ccl the caller's class loader
 237      * @param interfaces the list of interfaces that a proxy class implements
 238      */
 239     public static void checkProxyPackageAccess(ClassLoader ccl,
 240                                                Class<?>... interfaces)
 241     {
 242         SecurityManager sm = System.getSecurityManager();
 243         if (sm != null) {
 244             for (Class<?> intf : interfaces) {
 245                 ClassLoader cl = intf.getClassLoader();
 246                 if (needsPackageAccessCheck(ccl, cl)) {
 247                     privateCheckPackageAccess(sm, intf);
 248                 }
 249             }
 250         }
 251     }
 252 
 253     // Note that bytecode instrumentation tools may exclude 'sun.*'
 254     // classes but not generated proxy classes and so keep it in com.sun.*
 255     public static final String PROXY_PACKAGE = "com.sun.proxy";
 256 
 257     /**
 258      * Test if the given class is a proxy class that implements
 259      * non-public interface.  Such proxy class may be in a non-restricted
 260      * package that bypasses checkPackageAccess.
 261      */
 262     public static boolean isNonPublicProxyClass(Class<?> cls) {
 263         if (!Proxy.isProxyClass(cls)) {
 264             return false;
 265         }
 266         String pkg = cls.getPackageName();
 267         return pkg == null || !pkg.startsWith(PROXY_PACKAGE);
 268     }
 269 
 270     /**
 271      * Check if the given method is a method declared in the proxy interface
 272      * implemented by the given proxy instance.
 273      *
 274      * @param proxy a proxy instance
 275      * @param method an interface method dispatched to a InvocationHandler
 276      *
 277      * @throws IllegalArgumentException if the given proxy or method is invalid.
 278      */
 279     public static void checkProxyMethod(Object proxy, Method method) {
 280         // check if it is a valid proxy instance
 281         if (proxy == null || !Proxy.isProxyClass(proxy.getClass())) {
 282             throw new IllegalArgumentException("Not a Proxy instance");
 283         }
 284         if (Modifier.isStatic(method.getModifiers())) {
 285             throw new IllegalArgumentException("Can't handle static method");
 286         }
 287 
 288         Class<?> c = method.getDeclaringClass();
 289         if (c == Object.class) {
 290             String name = method.getName();
 291             if (name.equals("hashCode") || name.equals("equals") || name.equals("toString")) {
 292                 return;
 293             }
 294         }
 295 
 296         if (isSuperInterface(proxy.getClass(), c)) {
 297             return;
 298         }
 299 
 300         // disallow any method not declared in one of the proxy interfaces
 301         throw new IllegalArgumentException("Can't handle: " + method);
 302     }
 303 
 304     private static boolean isSuperInterface(Class<?> c, Class<?> intf) {
 305         for (Class<?> i : c.getInterfaces()) {
 306             if (i == intf) {
 307                 return true;
 308             }
 309             if (isSuperInterface(i, intf)) {
 310                 return true;
 311             }
 312         }
 313         return false;
 314     }
 315 }