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.linker;
27
28import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
29
30import java.lang.reflect.Modifier;
31import java.lang.reflect.Proxy;
32import jdk.dynalink.CallSiteDescriptor;
33import jdk.dynalink.StandardNamespace;
34import jdk.dynalink.StandardOperation;
35import jdk.dynalink.linker.GuardedInvocation;
36import jdk.dynalink.linker.LinkRequest;
37import jdk.dynalink.linker.LinkerServices;
38import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker;
39import jdk.nashorn.api.scripting.ClassFilter;
40import jdk.nashorn.internal.objects.Global;
41import jdk.nashorn.internal.runtime.Context;
42
43/**
44 * Check java reflection permission for java reflective and java.lang.invoke access from scripts
45 */
46final class ReflectionCheckLinker implements TypeBasedGuardingDynamicLinker{
47    private static final Class<?> STATEMENT_CLASS  = getBeanClass("Statement");
48    private static final Class<?> XMLENCODER_CLASS = getBeanClass("XMLEncoder");
49    private static final Class<?> XMLDECODER_CLASS = getBeanClass("XMLDecoder");
50
51    private static Class<?> getBeanClass(final String name) {
52        try {
53            return Class.forName("java.beans." + name);
54        } catch (final ClassNotFoundException cnfe) {
55            // Possible to miss this class in other profiles.
56            return null;
57        }
58    }
59
60    @Override
61    public boolean canLinkType(final Class<?> type) {
62        return isReflectionClass(type);
63    }
64
65    private static boolean isReflectionClass(final Class<?> type) {
66        // Class or ClassLoader subclasses
67        if (type == Class.class || ClassLoader.class.isAssignableFrom(type)) {
68            return true;
69        }
70
71        // check for bean reflection
72        if ((STATEMENT_CLASS != null && STATEMENT_CLASS.isAssignableFrom(type)) ||
73            (XMLENCODER_CLASS != null && XMLENCODER_CLASS.isAssignableFrom(type)) ||
74            (XMLDECODER_CLASS != null && XMLDECODER_CLASS.isAssignableFrom(type))) {
75            return true;
76        }
77
78        // package name check
79        final String name = type.getName();
80        return name.startsWith("java.lang.reflect.") || name.startsWith("java.lang.invoke.");
81    }
82
83    @Override
84    public GuardedInvocation getGuardedInvocation(final LinkRequest origRequest, final LinkerServices linkerServices)
85            throws Exception {
86        checkLinkRequest(origRequest);
87        // let the next linker deal with actual linking
88        return null;
89    }
90
91    private static boolean isReflectiveCheckNeeded(final Class<?> type, final boolean isStatic) {
92         // special handling for Proxy subclasses
93         if (Proxy.class.isAssignableFrom(type)) {
94            if (Proxy.isProxyClass(type)) {
95                // real Proxy class - filter only static access
96                return isStatic;
97            }
98
99            // fake Proxy subclass - filter it always!
100            return true;
101        }
102
103        // check for any other reflective Class
104        return isReflectionClass(type);
105    }
106
107    static void checkReflectionAccess(final Class<?> clazz, final boolean isStatic) {
108        final Global global = Context.getGlobal();
109        final ClassFilter cf = global.getClassFilter();
110        if (cf != null && isReflectiveCheckNeeded(clazz, isStatic)) {
111            throw typeError("no.reflection.with.classfilter");
112        }
113
114        final SecurityManager sm = System.getSecurityManager();
115        if (sm != null && isReflectiveCheckNeeded(clazz, isStatic)) {
116            checkReflectionPermission(sm);
117        }
118    }
119
120    private static void checkLinkRequest(final LinkRequest request) {
121        final Global global = Context.getGlobal();
122        final ClassFilter cf = global.getClassFilter();
123        if (cf != null) {
124            throw typeError("no.reflection.with.classfilter");
125        }
126
127        final SecurityManager sm = System.getSecurityManager();
128        if (sm != null) {
129            final Object self = request.getReceiver();
130            // allow 'static' access on Class objects representing public classes of non-restricted packages
131            if ((self instanceof Class) && Modifier.isPublic(((Class<?>)self).getModifiers())) {
132                final CallSiteDescriptor desc = request.getCallSiteDescriptor();
133                if ("static".equals(NashornCallSiteDescriptor.getOperand(desc)) && NashornCallSiteDescriptor.contains(desc, StandardOperation.GET, StandardNamespace.PROPERTY)) {
134                    if (Context.isAccessibleClass((Class<?>)self) && !isReflectionClass((Class<?>)self)) {
135                        // If "GET:PROPERTY:static" passes access checks, allow access.
136                        return;
137                    }
138                }
139            }
140            checkReflectionPermission(sm);
141        }
142    }
143
144    private static void checkReflectionPermission(final SecurityManager sm) {
145        sm.checkPermission(new RuntimePermission(Context.NASHORN_JAVA_REFLECTION));
146    }
147}
148