1 /*
   2  * Copyright (c) 2013, 2017, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.lang.reflect.Constructor;
  25 import java.lang.reflect.InvocationHandler;
  26 import java.lang.reflect.Method;
  27 import java.lang.reflect.Modifier;
  28 import java.lang.reflect.Proxy;
  29 import java.lang.reflect.ReflectPermission;
  30 import java.security.AccessControlException;
  31 import java.security.CodeSource;
  32 import java.security.Permission;
  33 import java.security.PermissionCollection;
  34 import java.security.Permissions;
  35 import java.security.Policy;
  36 import java.security.ProtectionDomain;
  37 import java.security.SecurityPermission;
  38 import java.util.*;
  39 
  40 /*
  41  * @test
  42  * @bug 8004260 8189291
  43  * @summary Test proxy classes that implement non-public interface
  44  *
  45  * @build p.Foo
  46  * @run main/othervm NonPublicProxyClass grant
  47  * @run main/othervm NonPublicProxyClass deny
  48  * @run main/othervm NonPublicProxyClass
  49  */
  50 public class NonPublicProxyClass {
  51     static final Policy DEFAULT_POLICY = Policy.getPolicy();
  52 
  53     public interface PublicInterface {
  54         void foo();
  55     }
  56     interface NonPublicInterface {
  57         void bar();
  58     }
  59 
  60     public static void main(String[] args) throws Exception {
  61         ClassLoader loader = ClassLoader.getSystemClassLoader();
  62         Class<?> zipConstantsClass = Class.forName("java.util.zip.ZipConstants", false, null);
  63         Class<?> fooClass = Class.forName("p.Foo");
  64 
  65         NonPublicProxyClass test1 =
  66             new NonPublicProxyClass(loader, PublicInterface.class, NonPublicInterface.class);
  67         NonPublicProxyClass test2 =
  68             new NonPublicProxyClass(loader, fooClass, PublicInterface.class);
  69         NonPublicProxyClass test3 =
  70             new NonPublicProxyClass(null, zipConstantsClass);
  71 
  72         if (args.length == 1) {
  73             switch (args[0]) {
  74                 case "grant": Policy.setPolicy(new NewInstancePolicy(true));
  75                               break;
  76                 case "deny" : Policy.setPolicy(new NewInstancePolicy(false));
  77                               break;
  78                 default: throw new IllegalArgumentException(args[0]);
  79             }
  80             System.setSecurityManager(new SecurityManager());
  81         }
  82 
  83         test1.run();
  84         test2.run();
  85         test3.run();
  86         System.out.format("Test passed: security %s%n",
  87             (args.length == 0 ? "manager not installed" : Policy.getPolicy()));
  88     }
  89 
  90     private final ClassLoader loader;
  91     private final Class<?>[] interfaces;
  92     private final InvocationHandler handler = newInvocationHandler();
  93     private Class<?> proxyClass;
  94     public NonPublicProxyClass(ClassLoader loader, Class<?> ... intfs) {
  95         this.loader = loader;
  96         this.interfaces = intfs;
  97     }
  98 
  99     public void run() throws Exception {
 100         boolean hasAccess = loader != null || hasAccess();
 101         try {
 102             proxyClass = Proxy.getProxyClass(loader, interfaces);
 103             if (!hasAccess) {
 104                 throw new RuntimeException("should have no permission to create proxy class");
 105             }
 106         } catch (AccessControlException e) {
 107             if (hasAccess) {
 108                 throw e;
 109             }
 110             if (e.getPermission().getClass() != RuntimePermission.class ||
 111                     !e.getPermission().getName().equals("getClassLoader")) {
 112                 throw e;
 113             }
 114             return;
 115         }
 116 
 117         if (Modifier.isPublic(proxyClass.getModifiers())) {
 118             throw new RuntimeException(proxyClass + " must be non-public");
 119         }
 120         newProxyInstance();
 121         newInstanceFromConstructor(proxyClass);
 122     }
 123 
 124     private boolean hasAccess() {
 125         if (System.getSecurityManager() == null) {
 126             return true;
 127         }
 128         NewInstancePolicy policy = NewInstancePolicy.class.cast(Policy.getPolicy());
 129         return policy.grant;
 130     }
 131 
 132     private static final String NEW_PROXY_IN_PKG = "newProxyInPackage.";
 133     private void newProxyInstance() {
 134         // expect newProxyInstance to succeed if it's in the same runtime package
 135         int i = proxyClass.getName().lastIndexOf('.');
 136         String pkg = (i != -1) ? proxyClass.getName().substring(0, i) : "";
 137         boolean hasAccess = pkg.isEmpty() || hasAccess();
 138         try {
 139             Proxy.newProxyInstance(loader, interfaces, handler);
 140             if (!hasAccess) {
 141                 throw new RuntimeException("ERROR: Proxy.newProxyInstance should fail " + proxyClass);
 142             }
 143         } catch (AccessControlException e) {
 144             if (hasAccess) {
 145                 throw e;
 146             }
 147             if (e.getPermission().getClass() != ReflectPermission.class ||
 148                     !e.getPermission().getName().equals(NEW_PROXY_IN_PKG + pkg)) {
 149                 throw e;
 150             }
 151         }
 152     }
 153 
 154     private void newInstanceFromConstructor(Class<?> proxyClass)
 155         throws Exception
 156     {
 157         // expect newInstance to succeed if it's in the same runtime package
 158         boolean isSamePackage = proxyClass.getName().lastIndexOf('.') == -1;
 159         try {
 160             Constructor cons = proxyClass.getConstructor(InvocationHandler.class);
 161             cons.newInstance(newInvocationHandler());
 162             if (!isSamePackage) {
 163                 throw new RuntimeException("ERROR: Constructor.newInstance should not succeed");
 164             }
 165         }  catch (IllegalAccessException e) {
 166             if (isSamePackage) {
 167                 throw e;
 168             }
 169         }
 170     }
 171 
 172     private static InvocationHandler newInvocationHandler() {
 173         return new InvocationHandler() {
 174             public Object invoke(Object proxy, Method method, Object[] args)
 175                     throws Throwable {
 176                 Class<?>[] intfs = proxy.getClass().getInterfaces();
 177                 System.out.println("Proxy for " + Arrays.toString(intfs)
 178                         + " " + method.getName() + " is being invoked");
 179                 return null;
 180             }
 181         };
 182     }
 183 
 184     static class NewInstancePolicy extends Policy {
 185         final PermissionCollection permissions = new Permissions();
 186         final boolean grant;
 187         NewInstancePolicy(boolean grant) {
 188             this.grant = grant;
 189             permissions.add(new SecurityPermission("getPolicy"));
 190             if (grant) {
 191                 permissions.add(new RuntimePermission("getClassLoader"));
 192                 permissions.add(new ReflectPermission(NEW_PROXY_IN_PKG + "p"));
 193                 permissions.add(new ReflectPermission(NEW_PROXY_IN_PKG + "java.util.zip"));
 194             }
 195         }
 196         public PermissionCollection getPermissions(ProtectionDomain domain) {
 197             return permissions;
 198         }
 199 
 200         public PermissionCollection getPermissions(CodeSource codesource) {
 201             return permissions;
 202         }
 203 
 204         public boolean implies(ProtectionDomain domain, Permission perm) {
 205             return permissions.implies(perm) ||
 206                     DEFAULT_POLICY.implies(domain, perm);
 207         }
 208 
 209         public String toString() {
 210             StringBuilder sb = new StringBuilder("policy: ");
 211             Enumeration<Permission> perms = permissions.elements();
 212             while (perms.hasMoreElements()) {
 213                 sb.append("\n").append(perms.nextElement().toString());
 214             }
 215             return sb.toString();
 216         }
 217     }
 218 }