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
26package jdk.nashorn.internal.runtime;
27
28import java.io.File;
29import java.io.InputStream;
30import java.io.IOException;
31import java.io.UncheckedIOException;
32import java.lang.reflect.InvocationTargetException;
33import java.lang.reflect.Method;
34import java.net.MalformedURLException;
35import java.net.URL;
36import java.net.URLClassLoader;
37import java.security.AccessController;
38import java.security.CodeSource;
39import java.security.Permission;
40import java.security.PermissionCollection;
41import java.security.PrivilegedAction;
42import java.security.Permissions;
43import java.security.SecureClassLoader;
44
45/**
46 * Superclass for Nashorn class loader classes.
47 */
48abstract 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
231