/
githubmirror
/
panama-vector
Обзор
Документация
Войти
/
githubmirror
/
panama-vector
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/java.rmi/share/classes/sun/rmi/server/LoaderHandler.java
545 строк
21 KB
Sean Mullan
8338411: Implement JEP 486: Permanently Disable the Security Manager
12 ноя 2024, 20:16
12 ноя 2024, 20:16
db85090
Код
Авторство
О чём код?
/* * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.rmi.server; import java.lang.ref.SoftReference; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.rmi.server.LogStream; import java.util.Arrays; import java.util.Collections; import java.util.IdentityHashMap; import java.util.Map; import java.util.StringTokenizer; import java.util.WeakHashMap; import sun.rmi.runtime.Log; /** * <code>LoaderHandler</code> provides the implementation of the static * methods of the <code>java.rmi.server.RMIClassLoader</code> class. * * @author Ann Wollrath * @author Peter Jones * @author Laird Dornin */ @SuppressWarnings("deprecation") public final class LoaderHandler { /** RMI class loader log level */ static final int logLevel = LogStream.parseLevel(System.getProperty("sun.rmi.loader.logLevel")); /* loader system log */ static final Log loaderLog = Log.getLog("sun.rmi.loader", "loader", LoaderHandler.logLevel); /** * value of "java.rmi.server.codebase" property, as cached at class * initialization time. It may contain malformed URLs. */ private static String codebaseProperty = null; static { String prop = System.getProperty("java.rmi.server.codebase"); if (prop != null && prop.trim().length() > 0) { codebaseProperty = prop; } } /** list of URLs represented by the codebase property, if valid */ private static URL[] codebaseURLs = null; /** table of class loaders that use codebase property for annotation */ private static final Map<ClassLoader, Void> codebaseLoaders = Collections.synchronizedMap(new IdentityHashMap<ClassLoader, Void>(5)); static { for (ClassLoader codebaseLoader = ClassLoader.getSystemClassLoader(); codebaseLoader != null; codebaseLoader = codebaseLoader.getParent()) { codebaseLoaders.put(codebaseLoader, null); } } /* * Disallow anyone from creating one of these. */ private LoaderHandler() {} /** * Returns an array of URLs initialized with the value of the * java.rmi.server.codebase property as the URL path. */ private static synchronized URL[] getDefaultCodebaseURLs() throws MalformedURLException { /* * If it hasn't already been done, convert the codebase property * into an array of URLs; this may throw a MalformedURLException. */ if (codebaseURLs == null) { if (codebaseProperty != null) { codebaseURLs = pathToURLs(codebaseProperty); } else { codebaseURLs = new URL[0]; } } return codebaseURLs; } /** * Load a class from a network location (one or more URLs), * but first try to resolve the named class through the given * "default loader". */ public static Class<?> loadClass(String codebase, String name, ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException { if (loaderLog.isLoggable(Log.BRIEF)) { loaderLog.log(Log.BRIEF, "name = \"" + name + "\", " + "codebase = \"" + (codebase != null ? codebase : "") + "\"" + (defaultLoader != null ? ", defaultLoader = " + defaultLoader : "")); } URL[] urls; if (codebase != null) { urls = pathToURLs(codebase); } else { urls = getDefaultCodebaseURLs(); } if (defaultLoader != null) { try { Class<?> c = Class.forName(name, false, defaultLoader); if (loaderLog.isLoggable(Log.VERBOSE)) { loaderLog.log(Log.VERBOSE, "class \"" + name + "\" found via defaultLoader, " + "defined by " + c.getClassLoader()); } return c; } catch (ClassNotFoundException e) { } } return loadClass(urls, name); } /** * Returns the class annotation (representing the location for * a class) that RMI will use to annotate the call stream when * marshalling objects of the given class. */ public static String getClassAnnotation(Class<?> cl) { String name = cl.getName(); /* * Class objects for arrays of primitive types never need an * annotation, because they never need to be (or can be) downloaded. * * REMIND: should we (not) be annotating classes that are in * "java.*" packages? */ int nameLength = name.length(); if (nameLength > 0 && name.charAt(0) == '[') { // skip past all '[' characters (see bugid 4211906) int i = 1; while (nameLength > i && name.charAt(i) == '[') { i++; } if (nameLength > i && name.charAt(i) != 'L') { return null; } } /* * Get the class's class loader. If it is null, the system class * loader, an ancestor of the base class loader (such as the loader * for installed extensions), return the value of the * "java.rmi.server.codebase" property. */ ClassLoader loader = cl.getClassLoader(); if (loader == null || codebaseLoaders.containsKey(loader)) { return codebaseProperty; } /* * Get the codebase URL path for the class loader, if it supports * such a notion (i.e., if it is a URLClassLoader or subclass). */ String annotation = null; if (loader instanceof URLClassLoader) { URL[] urls = ((URLClassLoader) loader).getURLs(); if (urls != null) { annotation = urlsToPath(urls); } } if (annotation != null) { return annotation; } else { return codebaseProperty; // REMIND: does this make sense?? } } /** * Returns the thread context classloader. The codebase argument is ignored. */ public static ClassLoader getClassLoader(String codebase) throws MalformedURLException { URL[] urls; // ignored, used only for URL syntax checking if (codebase != null) { urls = pathToURLs(codebase); } else { urls = getDefaultCodebaseURLs(); } return Thread.currentThread().getContextClassLoader(); } /** * Register a class loader as one whose classes should always be * annotated with the value of the "java.rmi.server.codebase" property. */ public static void registerCodebaseLoader(ClassLoader loader) { codebaseLoaders.put(loader, null); } /** * Load a class from the RMI class loader corresponding to the given * codebase URL path in the current execution context. */ private static Class<?> loadClass(URL[] urls, String name) throws ClassNotFoundException { ClassLoader parent = Thread.currentThread().getContextClassLoader(); if (loaderLog.isLoggable(Log.VERBOSE)) { loaderLog.log(Log.VERBOSE, "(thread context class loader: " + parent + ")"); } /* * There is no security manager, so disable access to RMI class * loaders and simply delegate request to the parent loader * (see bugid 4140511). */ try { Class<?> c = Class.forName(name, false, parent); if (loaderLog.isLoggable(Log.VERBOSE)) { loaderLog.log(Log.VERBOSE, "class \"" + name + "\" found via " + "thread context class loader " + "(no security manager: codebase disabled), " + "defined by " + c.getClassLoader()); } return c; } catch (ClassNotFoundException e) { if (loaderLog.isLoggable(Log.BRIEF)) { loaderLog.log(Log.BRIEF, "class \"" + name + "\" not found via " + "thread context class loader " + "(no security manager: codebase disabled)", e); } throw new ClassNotFoundException(e.getMessage() + " (no security manager: RMI class loader disabled)", e.getException()); } } /** * Define and return a dynamic proxy class in a class loader with * URLs supplied in the given location. The proxy class will * implement interface classes named by the given array of * interface names. */ public static Class<?> loadProxyClass(String codebase, String[] interfaces, ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException { if (loaderLog.isLoggable(Log.BRIEF)) { loaderLog.log(Log.BRIEF, "interfaces = " + Arrays.asList(interfaces) + ", " + "codebase = \"" + (codebase != null ? codebase : "") + "\"" + (defaultLoader != null ? ", defaultLoader = " + defaultLoader : "")); } /* * This method uses a fairly complex algorithm to load the * proxy class and its interface classes in order to maximize * the likelihood that the proxy's codebase annotation will be * preserved. The algorithm is (assuming that all of the * proxy interface classes are public): * * If the default loader is not null, try to load the proxy * interfaces through that loader. If the interfaces can be * loaded in that loader, try to define the proxy class in an * RMI class loader (child of the context class loader) before * trying to define the proxy in the default loader. If the * attempt to define the proxy class succeeds, the codebase * annotation is preserved. If the attempt fails, try to * define the proxy class in the default loader. * * If the interface classes can not be loaded from the default * loader or the default loader is null, try to load them from * the RMI class loader. Then try to define the proxy class * in the RMI class loader. * * Additionally, if any of the proxy interface classes are not * public, all of the non-public interfaces must reside in the * same class loader or it will be impossible to define the * proxy class (an IllegalAccessError will be thrown). An * attempt to load the interfaces from the default loader is * made. If the attempt fails, a second attempt will be made * to load the interfaces from the RMI loader. If all of the * non-public interfaces classes do reside in the same class * loader, then we attempt to define the proxy class in the * class loader of the non-public interfaces. No other * attempt to define the proxy class will be made. */ ClassLoader parent = Thread.currentThread().getContextClassLoader(); if (loaderLog.isLoggable(Log.VERBOSE)) { loaderLog.log(Log.VERBOSE, "(thread context class loader: " + parent + ")"); } URL[] urls; if (codebase != null) { urls = pathToURLs(codebase); } else { urls = getDefaultCodebaseURLs(); } /* * There is no security manager, so disable access to RMI class * loaders and use the would-be parent instead. */ try { Class<?> c = loadProxyClass(interfaces, defaultLoader, parent, false); if (loaderLog.isLoggable(Log.VERBOSE)) { loaderLog.log(Log.VERBOSE, "(no security manager: codebase disabled) " + "proxy class defined by " + c.getClassLoader()); } return c; } catch (ClassNotFoundException e) { if (loaderLog.isLoggable(Log.BRIEF)) { loaderLog.log(Log.BRIEF, "(no security manager: codebase disabled) " + "proxy class resolution failed", e); } throw new ClassNotFoundException(e.getMessage() + " (no security manager: RMI class loader disabled)", e.getException()); } } /** * Define a proxy class in the default loader if appropriate. * Define the class in an RMI class loader otherwise. The proxy * class will implement classes which are named in the supplied * interfaceNames. */ private static Class<?> loadProxyClass(String[] interfaceNames, ClassLoader defaultLoader, ClassLoader codebaseLoader, boolean preferCodebase) throws ClassNotFoundException { ClassLoader proxyLoader = null; Class<?>[] classObjs = new Class<?>[interfaceNames.length]; boolean[] nonpublic = { false }; defaultLoaderCase: if (defaultLoader != null) { try { proxyLoader = loadProxyInterfaces(interfaceNames, defaultLoader, classObjs, nonpublic); if (loaderLog.isLoggable(Log.VERBOSE)) { ClassLoader[] definingLoaders = new ClassLoader[classObjs.length]; for (int i = 0; i < definingLoaders.length; i++) { definingLoaders[i] = classObjs[i].getClassLoader(); } loaderLog.log(Log.VERBOSE, "proxy interfaces found via defaultLoader, " + "defined by " + Arrays.asList(definingLoaders)); } } catch (ClassNotFoundException e) { break defaultLoaderCase; } if (!nonpublic[0]) { if (preferCodebase) { try { return Proxy.getProxyClass(codebaseLoader, classObjs); } catch (IllegalArgumentException e) { } } proxyLoader = defaultLoader; } return loadProxyClass(proxyLoader, classObjs); } nonpublic[0] = false; proxyLoader = loadProxyInterfaces(interfaceNames, codebaseLoader, classObjs, nonpublic); if (loaderLog.isLoggable(Log.VERBOSE)) { ClassLoader[] definingLoaders = new ClassLoader[classObjs.length]; for (int i = 0; i < definingLoaders.length; i++) { definingLoaders[i] = classObjs[i].getClassLoader(); } loaderLog.log(Log.VERBOSE, "proxy interfaces found via codebase, " + "defined by " + Arrays.asList(definingLoaders)); } if (!nonpublic[0]) { proxyLoader = codebaseLoader; } return loadProxyClass(proxyLoader, classObjs); } /** * Define a proxy class in the given class loader. The proxy * class will implement the given interfaces Classes. */ private static Class<?> loadProxyClass(ClassLoader loader, Class<?>[] interfaces) throws ClassNotFoundException { try { return Proxy.getProxyClass(loader, interfaces); } catch (IllegalArgumentException e) { throw new ClassNotFoundException( "error creating dynamic proxy class", e); } } /* * Load Class objects for the names in the interfaces array from * the given class loader. * * We pass classObjs and nonpublic arrays to avoid needing a * multi-element return value. nonpublic is an array to enable * the method to take a boolean argument by reference. * * nonpublic array is needed to signal when the return value of * this method should be used as the proxy class loader. Because * null represents a valid class loader, that value is * insufficient to signal that the return value should not be used * as the proxy class loader. */ private static ClassLoader loadProxyInterfaces(String[] interfaces, ClassLoader loader, Class<?>[] classObjs, boolean[] nonpublic) throws ClassNotFoundException { /* loader of a non-public interface class */ ClassLoader nonpublicLoader = null; for (int i = 0; i < interfaces.length; i++) { Class<?> cl = (classObjs[i] = Class.forName(interfaces[i], false, loader)); if (!Modifier.isPublic(cl.getModifiers())) { ClassLoader current = cl.getClassLoader(); if (loaderLog.isLoggable(Log.VERBOSE)) { loaderLog.log(Log.VERBOSE, "non-public interface \"" + interfaces[i] + "\" defined by " + current); } if (!nonpublic[0]) { nonpublicLoader = current; nonpublic[0] = true; } else if (current != nonpublicLoader) { throw new IllegalAccessError( "non-public interfaces defined in different " + "class loaders"); } } } return nonpublicLoader; } /** * Convert a string containing a space-separated list of URLs into a * corresponding array of URL objects, throwing a MalformedURLException * if any of the URLs are invalid. */ private static URL[] pathToURLs(String path) throws MalformedURLException { synchronized (pathToURLsCache) { Object[] v = pathToURLsCache.get(path); if (v != null) { return ((URL[])v[0]); } } StringTokenizer st = new StringTokenizer(path); // divide by spaces URL[] urls = new URL[st.countTokens()]; for (int i = 0; st.hasMoreTokens(); i++) { @SuppressWarnings("deprecation") var url = new URL(st.nextToken()); urls[i] = url; } synchronized (pathToURLsCache) { pathToURLsCache.put(path, new Object[] {urls, new SoftReference<String>(path)}); } return urls; } /** map from weak(key=string) to [URL[], soft(key)] */ private static final Map<String, Object[]> pathToURLsCache = new WeakHashMap<>(5); /** * Convert an array of URL objects into a corresponding string * containing a space-separated list of URLs. * * Note that if the array has zero elements, the return value is * null, not the empty string. */ private static String urlsToPath(URL[] urls) { if (urls.length == 0) { return null; } else if (urls.length == 1) { return urls[0].toExternalForm(); } else { StringBuilder path = new StringBuilder(urls[0].toExternalForm()); for (int i = 1; i < urls.length; i++) { path.append(' '); path.append(urls[i].toExternalForm()); } return path.toString(); } } }