/* * Copyright (c) 2010, 2013, 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 jdk.nashorn.internal.runtime; import java.io.File; import java.io.InputStream; import java.io.IOException; import java.io.UncheckedIOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.lang.reflect.Module; import java.security.AccessController; import java.security.CodeSource; import java.security.Permission; import java.security.PermissionCollection; import java.security.PrivilegedAction; import java.security.Permissions; import java.security.SecureClassLoader; /** * Superclass for Nashorn class loader classes. */ abstract class NashornLoader extends SecureClassLoader { protected static final String OBJECTS_PKG = "jdk.nashorn.internal.objects"; protected static final String RUNTIME_PKG = "jdk.nashorn.internal.runtime"; protected static final String RUNTIME_ARRAYS_PKG = "jdk.nashorn.internal.runtime.arrays"; protected static final String RUNTIME_LINKER_PKG = "jdk.nashorn.internal.runtime.linker"; protected static final String SCRIPTS_PKG = "jdk.nashorn.internal.scripts"; protected static final String OBJECTS_PKG_INTERNAL = "jdk/nashorn/internal/objects"; protected static final String RUNTIME_PKG_INTERNAL = "jdk/nashorn/internal/runtime"; protected static final String RUNTIME_ARRAYS_PKG_INTERNAL = "jdk/nashorn/internal/runtime/arrays"; protected static final String RUNTIME_LINKER_PKG_INTERNAL = "jdk/nashorn/internal/runtime/linker"; protected static final String SCRIPTS_PKG_INTERNAL = "jdk/nashorn/internal/scripts"; static final Module NASHORN_MODULE = Context.class.getModule(); private static final Permission[] SCRIPT_PERMISSIONS; private static final String MODULE_MANIPULATOR_NAME = SCRIPTS_PKG + ".ModuleGraphManipulator"; private static final byte[] MODULE_MANIPULATOR_BYTES = readModuleManipulatorBytes(); static { /* * Generated classes get access to runtime, runtime.linker, objects, scripts packages. * Note that the actual scripts can not access these because Java.type, Packages * prevent these restricted packages. And Java reflection and JSR292 access is prevented * for scripts. In other words, nashorn generated portions of script classes can access * classes in these implementation packages. */ SCRIPT_PERMISSIONS = new Permission[] { new RuntimePermission("accessClassInPackage." + RUNTIME_PKG), new RuntimePermission("accessClassInPackage." + RUNTIME_LINKER_PKG), new RuntimePermission("accessClassInPackage." + OBJECTS_PKG), new RuntimePermission("accessClassInPackage." + SCRIPTS_PKG), new RuntimePermission("accessClassInPackage." + RUNTIME_ARRAYS_PKG) }; } // addExport Method object on ModuleGraphManipulator // class loaded by this loader private Method addModuleExport; NashornLoader(final ClassLoader parent) { super(parent); } void loadModuleManipulator() { final Class<?> clazz = defineClass(MODULE_MANIPULATOR_NAME, MODULE_MANIPULATOR_BYTES, 0, MODULE_MANIPULATOR_BYTES.length); // force class initialization so that <clinit> runs! try { Class.forName(MODULE_MANIPULATOR_NAME, true, this); } catch (final Exception ex) { throw new RuntimeException(ex); } final PrivilegedAction<Void> pa = () -> { try { addModuleExport = clazz.getDeclaredMethod("addExport", Module.class); addModuleExport.setAccessible(true); } catch (final NoSuchMethodException | SecurityException ex) { throw new RuntimeException(ex); } return null; }; AccessController.doPrivileged(pa); } final void addModuleExport(final Module to) { try { addModuleExport.invoke(null, to); } catch (final IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { throw new RuntimeException(ex); } } protected static void checkPackageAccess(final String name) { final int i = name.lastIndexOf('.'); if (i != -1) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { final String pkgName = name.substring(0, i); switch (pkgName) { case RUNTIME_PKG: case RUNTIME_ARRAYS_PKG: case RUNTIME_LINKER_PKG: case OBJECTS_PKG: case SCRIPTS_PKG: // allow it. break; default: sm.checkPackageAccess(pkgName); } } } } @Override protected PermissionCollection getPermissions(final CodeSource codesource) { final Permissions permCollection = new Permissions(); for (final Permission perm : SCRIPT_PERMISSIONS) { permCollection.add(perm); } return permCollection; } /** * Create a secure URL class loader for the given classpath * @param classPath classpath for the loader to search from * @param parent the parent class loader for the new class loader * @return the class loader */ static ClassLoader createClassLoader(final String classPath, final ClassLoader parent) { final URL[] urls = pathToURLs(classPath); return URLClassLoader.newInstance(urls, parent); } /* * Utility method for converting a search path string to an array * of directory and JAR file URLs. * * @param path the search path string * @return the resulting array of directory and JAR file URLs */ private static URL[] pathToURLs(final String path) { final String[] components = path.split(File.pathSeparator); URL[] urls = new URL[components.length]; int count = 0; while(count < components.length) { final URL url = fileToURL(new File(components[count])); if (url != null) { urls[count++] = url; } } if (urls.length != count) { final URL[] tmp = new URL[count]; System.arraycopy(urls, 0, tmp, 0, count); urls = tmp; } return urls; } /* * Returns the directory or JAR file URL corresponding to the specified * local file name. * * @param file the File object * @return the resulting directory or JAR file URL, or null if unknown */ private static URL fileToURL(final File file) { String name; try { name = file.getCanonicalPath(); } catch (final IOException e) { name = file.getAbsolutePath(); } name = name.replace(File.separatorChar, '/'); if (!name.startsWith("/")) { name = "/" + name; } // If the file does not exist, then assume that it's a directory if (!file.isFile()) { name += "/"; } try { return new URL("file", "", name); } catch (final MalformedURLException e) { throw new IllegalArgumentException("file"); } } private static byte[] readModuleManipulatorBytes() { final PrivilegedAction<byte[]> pa = () -> { final String res = "/"+ MODULE_MANIPULATOR_NAME.replace('.', '/') + ".class"; try (InputStream in = NashornLoader.class.getResourceAsStream(res)) { return in.readAllBytes(); } catch (final IOException exp) { throw new UncheckedIOException(exp); } }; return AccessController.doPrivileged(pa); } }