1 /* 2 * Copyright (c) 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. 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 package com.sun.xml.internal.ws.policy.util; 27 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.lang.reflect.InvocationTargetException; 31 import java.lang.reflect.Method; 32 import java.lang.reflect.Proxy; 33 import java.security.AccessController; 34 import java.security.AllPermission; 35 import java.security.CodeSource; 36 import java.security.PermissionCollection; 37 import java.security.PrivilegedExceptionAction; 38 import java.security.SecureClassLoader; 39 import java.util.Arrays; 40 41 /* 42 * This copies from sun.reflect.misc.MethodUtil to implement the trampoline 43 * code such that when a Method is invoked, it will be called through 44 * the trampoline that is defined by this MethodUtil class loader. 45 */ 46 class Trampoline { 47 static { 48 if (Trampoline.class.getClassLoader() == null) { 49 throw new Error( 50 "Trampoline must not be defined by the bootstrap classloader"); 51 } 52 } 53 54 private static void ensureInvocableMethod(Method m) 55 throws InvocationTargetException 56 { 57 Class<?> clazz = m.getDeclaringClass(); 58 if (clazz.equals(AccessController.class) || 59 clazz.equals(Method.class) || 60 clazz.getName().startsWith("java.lang.invoke.")) 61 throw new InvocationTargetException( 62 new UnsupportedOperationException("invocation not supported")); 63 } 64 65 private static Object invoke(Method m, Object obj, Object[] params) 66 throws InvocationTargetException, IllegalAccessException 67 { 68 ensureInvocableMethod(m); 69 return m.invoke(obj, params); 70 } 71 } 72 73 /* 74 * Create a trampoline class. 75 */ 76 public final class MethodUtil extends SecureClassLoader { 77 private static final String WS_UTIL_POLICY_PKG = "com.sun.xml.internal.ws.policy.util."; 78 private static final String TRAMPOLINE = WS_UTIL_POLICY_PKG + "Trampoline"; 79 private static final Method bounce = getTrampoline(); 80 private static final int DEFAULT_BUFFER_SIZE = 8192; 81 private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; 82 83 84 private MethodUtil() { 85 super(); 86 } 87 88 /* 89 * Bounce through the trampoline. 90 */ 91 public static Object invoke(Method m, Object obj, Object[] params) 92 throws InvocationTargetException, IllegalAccessException { 93 try { 94 return bounce.invoke(null, m, obj, params); 95 } catch (InvocationTargetException ie) { 96 Throwable t = ie.getCause(); 97 98 if (t instanceof InvocationTargetException) { 99 throw (InvocationTargetException)t; 100 } else if (t instanceof IllegalAccessException) { 101 throw (IllegalAccessException)t; 102 } else if (t instanceof RuntimeException) { 103 throw (RuntimeException)t; 104 } else if (t instanceof Error) { 105 throw (Error)t; 106 } else { 107 throw new Error("Unexpected invocation error", t); 108 } 109 } catch (IllegalAccessException iae) { 110 // this can't happen 111 throw new Error("Unexpected invocation error", iae); 112 } 113 } 114 115 private static Method getTrampoline() { 116 try { 117 return AccessController.doPrivileged( 118 new PrivilegedExceptionAction<Method>() { 119 public Method run() throws Exception { 120 Class<?> t = getTrampolineClass(); 121 Method b = t.getDeclaredMethod("invoke", 122 Method.class, Object.class, Object[].class); 123 b.setAccessible(true); 124 return b; 125 } 126 }); 127 } catch (Exception e) { 128 throw new InternalError("bouncer cannot be found", e); 129 } 130 } 131 132 133 protected synchronized Class<?> loadClass(String name, boolean resolve) 134 throws ClassNotFoundException 135 { 136 // First, check if the class has already been loaded 137 checkPackageAccess(name); 138 Class<?> c = findLoadedClass(name); 139 if (c == null) { 140 try { 141 c = findClass(name); 142 } catch (ClassNotFoundException e) { 143 // Fall through ... 144 } 145 if (c == null) { 146 c = getParent().loadClass(name); 147 } 148 } 149 if (resolve) { 150 resolveClass(c); 151 } 152 return c; 153 } 154 155 156 protected Class<?> findClass(final String name) 157 throws ClassNotFoundException 158 { 159 if (!name.startsWith(WS_UTIL_POLICY_PKG)) { 160 throw new ClassNotFoundException(name); 161 } 162 String path = "/".concat(name.replace('.', '/').concat(".class")); 163 try (InputStream in = MethodUtil.class.getResourceAsStream(path)) { 164 byte[] b = readAllBytes(in); 165 return defineClass(name, b); 166 } catch (IOException e) { 167 throw new ClassNotFoundException(name, e); 168 } 169 } 170 171 /** 172 * JDK9 {@link InputStream#readAllBytes()} substitution. 173 */ 174 private byte[] readAllBytes(InputStream in) throws IOException { 175 byte[] buf = new byte[DEFAULT_BUFFER_SIZE]; 176 int capacity = buf.length; 177 int nread = 0; 178 int n; 179 for (;;) { 180 // read to EOF which may read more or less than initial buffer size 181 while ((n = in.read(buf, nread, capacity - nread)) > 0) 182 nread += n; 183 184 // if the last call to read returned -1, then we're done 185 if (n < 0) 186 break; 187 188 // need to allocate a larger buffer 189 if (capacity <= MAX_BUFFER_SIZE - capacity) { 190 capacity = capacity << 1; 191 } else { 192 if (capacity == MAX_BUFFER_SIZE) 193 throw new OutOfMemoryError("Required array size too large"); 194 capacity = MAX_BUFFER_SIZE; 195 } 196 buf = Arrays.copyOf(buf, capacity); 197 } 198 return (capacity == nread) ? buf : Arrays.copyOf(buf, nread); 199 } 200 201 202 203 /* 204 * Define the proxy classes 205 */ 206 private Class<?> defineClass(String name, byte[] b) throws IOException { 207 CodeSource cs = new CodeSource(null, (java.security.cert.Certificate[])null); 208 if (!name.equals(TRAMPOLINE)) { 209 throw new IOException("MethodUtil: bad name " + name); 210 } 211 return defineClass(name, b, 0, b.length, cs); 212 } 213 214 protected PermissionCollection getPermissions(CodeSource codesource) 215 { 216 PermissionCollection perms = super.getPermissions(codesource); 217 perms.add(new AllPermission()); 218 return perms; 219 } 220 221 private static Class<?> getTrampolineClass() { 222 try { 223 return Class.forName(TRAMPOLINE, true, new MethodUtil()); 224 } catch (ClassNotFoundException e) { 225 } 226 return null; 227 } 228 229 /** 230 * Checks package access on the given classname. 231 * This method is typically called when the Class instance is not 232 * available and the caller attempts to load a class on behalf 233 * the true caller (application). 234 */ 235 private static void checkPackageAccess(String name) { 236 SecurityManager s = System.getSecurityManager(); 237 if (s != null) { 238 String cname = name.replace('/', '.'); 239 if (cname.startsWith("[")) { 240 int b = cname.lastIndexOf('[') + 2; 241 if (b > 1 && b < cname.length()) { 242 cname = cname.substring(b); 243 } 244 } 245 int i = cname.lastIndexOf('.'); 246 if (i != -1) { 247 s.checkPackageAccess(cname.substring(0, i)); 248 } 249 } 250 } 251 252 /** 253 * Checks package access on the given class. 254 * 255 * If it is a {@link Proxy#isProxyClass(Class)} that implements 256 * a non-public interface (i.e. may be in a non-restricted package), 257 * also check the package access on the proxy interfaces. 258 */ 259 private static void checkPackageAccess(Class<?> clazz) { 260 checkPackageAccess(clazz.getName()); 261 if (isNonPublicProxyClass(clazz)) { 262 checkProxyPackageAccess(clazz); 263 } 264 } 265 266 // Note that bytecode instrumentation tools may exclude 'sun.*' 267 // classes but not generated proxy classes and so keep it in com.sun.* 268 private static final String PROXY_PACKAGE = "com.sun.proxy"; 269 270 /** 271 * Test if the given class is a proxy class that implements 272 * non-public interface. Such proxy class may be in a non-restricted 273 * package that bypasses checkPackageAccess. 274 */ 275 private static boolean isNonPublicProxyClass(Class<?> cls) { 276 String name = cls.getName(); 277 int i = name.lastIndexOf('.'); 278 String pkg = (i != -1) ? name.substring(0, i) : ""; 279 return Proxy.isProxyClass(cls) && !pkg.startsWith(PROXY_PACKAGE); 280 } 281 282 /** 283 * Check package access on the proxy interfaces that the given proxy class 284 * implements. 285 * 286 * @param clazz Proxy class object 287 */ 288 private static void checkProxyPackageAccess(Class<?> clazz) { 289 SecurityManager s = System.getSecurityManager(); 290 if (s != null) { 291 // check proxy interfaces if the given class is a proxy class 292 if (Proxy.isProxyClass(clazz)) { 293 for (Class<?> intf : clazz.getInterfaces()) { 294 checkPackageAccess(intf); 295 } 296 } 297 } 298 } 299 }