1 /* 2 * Copyright (c) 2010, 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 package jdk.nashorn.internal.runtime; 27 28 import java.io.File; 29 import java.io.InputStream; 30 import java.io.IOException; 31 import java.io.UncheckedIOException; 32 import java.lang.reflect.InvocationTargetException; 33 import java.lang.reflect.Method; 34 import java.net.MalformedURLException; 35 import java.net.URL; 36 import java.net.URLClassLoader; 37 import java.security.AccessController; 38 import java.security.CodeSource; 39 import java.security.Permission; 40 import java.security.PermissionCollection; 41 import java.security.PrivilegedAction; 42 import java.security.Permissions; 43 import java.security.SecureClassLoader; 44 45 /** 46 * Superclass for Nashorn class loader classes. 47 */ 48 abstract class NashornLoader extends SecureClassLoader { 49 protected static final String OBJECTS_PKG = "jdk.nashorn.internal.objects"; 50 protected static final String RUNTIME_PKG = "jdk.nashorn.internal.runtime"; 51 protected static final String RUNTIME_ARRAYS_PKG = "jdk.nashorn.internal.runtime.arrays"; 52 protected static final String RUNTIME_LINKER_PKG = "jdk.nashorn.internal.runtime.linker"; 53 protected static final String SCRIPTS_PKG = "jdk.nashorn.internal.scripts"; 54 protected static final String OBJECTS_PKG_INTERNAL = "jdk/nashorn/internal/objects"; 55 protected static final String RUNTIME_PKG_INTERNAL = "jdk/nashorn/internal/runtime"; 56 protected static final String RUNTIME_ARRAYS_PKG_INTERNAL = "jdk/nashorn/internal/runtime/arrays"; 57 protected static final String RUNTIME_LINKER_PKG_INTERNAL = "jdk/nashorn/internal/runtime/linker"; 58 protected static final String SCRIPTS_PKG_INTERNAL = "jdk/nashorn/internal/scripts"; 59 60 static final Module NASHORN_MODULE = Context.class.getModule(); 61 62 private static final Permission[] SCRIPT_PERMISSIONS; 63 64 private static final String MODULE_MANIPULATOR_NAME = SCRIPTS_PKG + ".ModuleGraphManipulator"; 65 private static final byte[] MODULE_MANIPULATOR_BYTES = readModuleManipulatorBytes(); 66 67 static { 68 /* 69 * Generated classes get access to runtime, runtime.linker, objects, scripts packages. 70 * Note that the actual scripts can not access these because Java.type, Packages 71 * prevent these restricted packages. And Java reflection and JSR292 access is prevented 72 * for scripts. In other words, nashorn generated portions of script classes can access 73 * classes in these implementation packages. 74 */ 75 SCRIPT_PERMISSIONS = new Permission[] { 76 new RuntimePermission("accessClassInPackage." + RUNTIME_PKG), 77 new RuntimePermission("accessClassInPackage." + RUNTIME_LINKER_PKG), 78 new RuntimePermission("accessClassInPackage." + OBJECTS_PKG), 79 new RuntimePermission("accessClassInPackage." + SCRIPTS_PKG), 80 new RuntimePermission("accessClassInPackage." + RUNTIME_ARRAYS_PKG) 81 }; 82 } 83 84 // addExport Method object on ModuleGraphManipulator 85 // class loaded by this loader 86 private Method addModuleExport; 87 88 NashornLoader(final ClassLoader parent) { 89 super(parent); 90 } 91 92 void loadModuleManipulator() { 93 final Class<?> clazz = defineClass(MODULE_MANIPULATOR_NAME, 94 MODULE_MANIPULATOR_BYTES, 0, MODULE_MANIPULATOR_BYTES.length); 95 // force class initialization so that <clinit> runs! 96 try { 97 Class.forName(MODULE_MANIPULATOR_NAME, true, this); 98 } catch (final Exception ex) { 99 throw new RuntimeException(ex); 100 } 101 final PrivilegedAction<Void> pa = () -> { 102 try { 103 addModuleExport = clazz.getDeclaredMethod("addExport", Module.class); 104 addModuleExport.setAccessible(true); 105 } catch (final NoSuchMethodException | SecurityException ex) { 106 throw new RuntimeException(ex); 107 } 108 return null; 109 }; 110 AccessController.doPrivileged(pa); 111 } 112 113 final void addModuleExport(final Module to) { 114 try { 115 addModuleExport.invoke(null, to); 116 } catch (final IllegalAccessException | 117 IllegalArgumentException | 118 InvocationTargetException ex) { 119 throw new RuntimeException(ex); 120 } 121 } 122 123 protected static void checkPackageAccess(final String name) { 124 final int i = name.lastIndexOf('.'); 125 if (i != -1) { 126 final SecurityManager sm = System.getSecurityManager(); 127 if (sm != null) { 128 final String pkgName = name.substring(0, i); 129 switch (pkgName) { 130 case RUNTIME_PKG: 131 case RUNTIME_ARRAYS_PKG: 132 case RUNTIME_LINKER_PKG: 133 case OBJECTS_PKG: 134 case SCRIPTS_PKG: 135 // allow it. 136 break; 137 default: 138 sm.checkPackageAccess(pkgName); 139 } 140 } 141 } 142 } 143 144 @Override 145 protected PermissionCollection getPermissions(final CodeSource codesource) { 146 final Permissions permCollection = new Permissions(); 147 for (final Permission perm : SCRIPT_PERMISSIONS) { 148 permCollection.add(perm); 149 } 150 return permCollection; 151 } 152 153 /** 154 * Create a secure URL class loader for the given classpath 155 * @param classPath classpath for the loader to search from 156 * @param parent the parent class loader for the new class loader 157 * @return the class loader 158 */ 159 static ClassLoader createClassLoader(final String classPath, final ClassLoader parent) { 160 final URL[] urls = pathToURLs(classPath); 161 return URLClassLoader.newInstance(urls, parent); 162 } 163 164 /* 165 * Utility method for converting a search path string to an array 166 * of directory and JAR file URLs. 167 * 168 * @param path the search path string 169 * @return the resulting array of directory and JAR file URLs 170 */ 171 private static URL[] pathToURLs(final String path) { 172 final String[] components = path.split(File.pathSeparator); 173 URL[] urls = new URL[components.length]; 174 int count = 0; 175 while(count < components.length) { 176 final URL url = fileToURL(new File(components[count])); 177 if (url != null) { 178 urls[count++] = url; 179 } 180 } 181 if (urls.length != count) { 182 final URL[] tmp = new URL[count]; 183 System.arraycopy(urls, 0, tmp, 0, count); 184 urls = tmp; 185 } 186 return urls; 187 } 188 189 /* 190 * Returns the directory or JAR file URL corresponding to the specified 191 * local file name. 192 * 193 * @param file the File object 194 * @return the resulting directory or JAR file URL, or null if unknown 195 */ 196 private static URL fileToURL(final File file) { 197 String name; 198 try { 199 name = file.getCanonicalPath(); 200 } catch (final IOException e) { 201 name = file.getAbsolutePath(); 202 } 203 name = name.replace(File.separatorChar, '/'); 204 if (!name.startsWith("/")) { 205 name = "/" + name; 206 } 207 // If the file does not exist, then assume that it's a directory 208 if (!file.isFile()) { 209 name += "/"; 210 } 211 try { 212 return new URL("file", "", name); 213 } catch (final MalformedURLException e) { 214 throw new IllegalArgumentException("file"); 215 } 216 } 217 218 private static byte[] readModuleManipulatorBytes() { 219 final PrivilegedAction<byte[]> pa = () -> { 220 final String res = "/"+ MODULE_MANIPULATOR_NAME.replace('.', '/') + ".class"; 221 try (InputStream in = NashornLoader.class.getResourceAsStream(res)) { 222 return in.readAllBytes(); 223 } catch (final IOException exp) { 224 throw new UncheckedIOException(exp); 225 } 226 }; 227 return AccessController.doPrivileged(pa); 228 } 229 } 230