src/java.desktop/share/classes/sun/applet/AppletSecurity.java
author mullan
Wed, 04 Apr 2018 11:26:10 -0400
changeset 49520 7a64b48586d8
parent 48027 ddbcfca4d51d
permissions -rw-r--r--
8193032: Remove terminally deprecated SecurityManager APIs Summary: Remove the checkTopLevelWindow, checkSystemClipboardAccess, checkAwtEventQueueAccess, and checkMemberAccess methods. Reviewed-by: alanb, mchung, kcr

/*
 * Copyright (c) 1995, 2018, 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.applet;

import java.io.File;
import java.io.FilePermission;
import java.io.IOException;
import java.io.FileDescriptor;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.net.SocketPermission;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.HashSet;
import java.util.StringTokenizer;
import java.security.*;
import java.lang.reflect.*;
import jdk.internal.misc.JavaNetURLClassLoaderAccess;
import jdk.internal.misc.JavaSecurityAccess;
import jdk.internal.misc.SharedSecrets;
import sun.awt.AWTSecurityManager;
import sun.awt.AppContext;
import sun.awt.AWTPermissions;
import sun.security.util.SecurityConstants;

import static java.lang.StackWalker.*;
import static java.lang.StackWalker.Option.*;


/**
 * This class defines an applet security policy
 *
 */
public
class AppletSecurity extends AWTSecurityManager {
    private static final JavaNetURLClassLoaderAccess JNUCLA
            = SharedSecrets.getJavaNetURLClassLoaderAccess();
    private static final JavaSecurityAccess JSA = SharedSecrets.getJavaSecurityAccess();

    /**
     * Construct and initialize.
     */
    public AppletSecurity() {
        reset();
    }

    // Cache to store known restricted packages
    private HashSet<String> restrictedPackages = new HashSet<>();

    /**
     * Reset from Properties
     */
    public void reset()
    {
        // Clear cache
        restrictedPackages.clear();

        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            public Object run()
            {
                // Enumerate system properties
                Enumeration<?> e = System.getProperties().propertyNames();

                while (e.hasMoreElements())
                {
                    String name = (String) e.nextElement();

                    if (name != null && name.startsWith("package.restrict.access."))
                    {
                        String value = System.getProperty(name);

                        if (value != null && value.equalsIgnoreCase("true"))
                        {
                            String pkg = name.substring(24);

                            // Cache restricted packages
                            restrictedPackages.add(pkg);
                        }
                    }
                }
                return null;
            }
        });
    }

    private static final StackWalker walker =
        AccessController.doPrivileged(
            (PrivilegedAction<StackWalker>) () ->
                StackWalker.getInstance(RETAIN_CLASS_REFERENCE));
    /**
     * Returns the class loader of the most recently executing method from
     * a class defined using a non-system class loader. A non-system
     * class loader is defined as being a class loader that is not equal to
     * the system class loader (as returned
     * by {@link ClassLoader#getSystemClassLoader}) or one of its ancestors.
     * <p>
     * This method will return
     * <code>null</code> in the following three cases:
     * <ol>
     *   <li>All methods on the execution stack are from classes
     *   defined using the system class loader or one of its ancestors.
     *
     *   <li>All methods on the execution stack up to the first
     *   "privileged" caller
     *   (see {@link java.security.AccessController#doPrivileged})
     *   are from classes
     *   defined using the system class loader or one of its ancestors.
     *
     *   <li> A call to <code>checkPermission</code> with
     *   <code>java.security.AllPermission</code> does not
     *   result in a SecurityException.
     * </ol>
     *
     * NOTE: This is an implementation of the SecurityManager.currentClassLoader
     * method that uses StackWalker. SecurityManager.currentClassLoader
     * has been removed from SE. This is a temporary workaround which is
     * only needed while applets are still supported.
     *
     * @return  the class loader of the most recent occurrence on the stack
     *          of a method from a class defined using a non-system class
     *          loader.
     */
    private static ClassLoader currentClassLoader() {
        StackFrame f =
            walker.walk(s -> s.takeWhile(AppletSecurity::isNonPrivileged)
                              .filter(AppletSecurity::isNonSystemFrame)
                              .findFirst())
                  .orElse(null);

        SecurityManager sm = System.getSecurityManager();
        if (f != null && sm != null) {
            try {
                sm.checkPermission(new AllPermission());
            } catch (SecurityException se) {
                return f.getDeclaringClass().getClassLoader();
            }
        }
        return null;
    }

    /**
     * Returns true if the StackFrame is not AccessController.doPrivileged.
     */
    private static boolean isNonPrivileged(StackFrame f) {
        // possibly other doPrivileged variants
        Class<?> c = f.getDeclaringClass();
        return c == AccessController.class &&
               f.getMethodName().equals("doPrivileged");
    }

    /**
     * Returns true if the StackFrame is not from a class defined by the
     * system class loader or one of its ancestors.
     */
    private static boolean isNonSystemFrame(StackFrame f) {
        ClassLoader loader = ClassLoader.getSystemClassLoader();
        ClassLoader ld = f.getDeclaringClass().getClassLoader();
        if (ld == null || ld == loader) return false;

        while ((loader = loader.getParent()) != null) {
            if (ld == loader)
                return false;
        }
        return true;
    }

    /**
     * get the current (first) instance of an AppletClassLoader on the stack.
     */
    private AppletClassLoader currentAppletClassLoader()
    {
        // try currentClassLoader first
        ClassLoader loader = currentClassLoader();

        if ((loader == null) || (loader instanceof AppletClassLoader))
            return (AppletClassLoader)loader;

        // if that fails, get all the classes on the stack and check them.
        Class<?>[] context = getClassContext();
        for (int i = 0; i < context.length; i++) {
            loader = context[i].getClassLoader();
            if (loader instanceof AppletClassLoader)
                return (AppletClassLoader)loader;
        }

        /*
         * fix bug # 6433620 the logic here is : try to find URLClassLoader from
         * class context, check its AccessControlContext to see if
         * AppletClassLoader is in stack when it's created. for this kind of
         * URLClassLoader, return the AppContext associated with the
         * AppletClassLoader.
         */
        for (int i = 0; i < context.length; i++) {
            final ClassLoader currentLoader = context[i].getClassLoader();

            if (currentLoader instanceof URLClassLoader) {
                URLClassLoader ld = (URLClassLoader)currentLoader;
                loader = AccessController.doPrivileged(
                    new PrivilegedAction<ClassLoader>() {
                        public ClassLoader run() {

                            AccessControlContext acc = null;
                            ProtectionDomain[] pds = null;

                            try {
                                acc = JNUCLA.getAccessControlContext(ld);
                                if (acc == null) {
                                    return null;
                                }

                                pds = JSA.getProtectDomains(acc);
                                if (pds == null) {
                                    return null;
                                }
                            } catch (Exception e) {
                                throw new UnsupportedOperationException(e);
                            }

                            for (int i=0; i<pds.length; i++) {
                                ClassLoader cl = pds[i].getClassLoader();

                                if (cl instanceof AppletClassLoader) {
                                        return cl;
                                }
                            }

                            return null;
                        }
                    });

                if (loader != null) {
                    return (AppletClassLoader) loader;
                }
            }
        }

        // if that fails, try the context class loader
        loader = Thread.currentThread().getContextClassLoader();
        if (loader instanceof AppletClassLoader)
            return (AppletClassLoader)loader;

        // no AppletClassLoaders on the stack
        return (AppletClassLoader)null;
    }

    /**
     * Returns true if this threadgroup is in the applet's own thread
     * group. This will return false if there is no current class
     * loader.
     */
    protected boolean inThreadGroup(ThreadGroup g) {
        if (currentAppletClassLoader() == null)
            return false;
        else
            return getThreadGroup().parentOf(g);
    }

    /**
     * Returns true of the threadgroup of thread is in the applet's
     * own threadgroup.
     */
    protected boolean inThreadGroup(Thread thread) {
        return inThreadGroup(thread.getThreadGroup());
    }

    /**
     * Applets are not allowed to manipulate threads outside
     * applet thread groups. However a terminated thread no longer belongs
     * to any group.
     */
    public void checkAccess(Thread t) {
        /* When multiple applets is reloaded simultaneously, there will be
         * multiple invocations to this method from plugin's SecurityManager.
         * This method should not be synchronized to avoid deadlock when
         * a page with multiple applets is reloaded
         */
        if ((t.getState() != Thread.State.TERMINATED) && !inThreadGroup(t)) {
            checkPermission(SecurityConstants.MODIFY_THREAD_PERMISSION);
        }
    }

    private boolean inThreadGroupCheck = false;

    /**
     * Applets are not allowed to manipulate thread groups outside
     * applet thread groups.
     */
    public synchronized void checkAccess(ThreadGroup g) {
        if (inThreadGroupCheck) {
            // if we are in a recursive check, it is because
            // inThreadGroup is calling appletLoader.getThreadGroup
            // in that case, only do the super check, as appletLoader
            // has a begin/endPrivileged
            checkPermission(SecurityConstants.MODIFY_THREADGROUP_PERMISSION);
        } else {
            try {
                inThreadGroupCheck = true;
                if (!inThreadGroup(g)) {
                    checkPermission(SecurityConstants.MODIFY_THREADGROUP_PERMISSION);
                }
            } finally {
                inThreadGroupCheck = false;
            }
        }
    }


    /**
     * Throws a {@code SecurityException} if the
     * calling thread is not allowed to access the package specified by
     * the argument.
     * <p>
     * This method is used by the {@code loadClass} method of class
     * loaders.
     * <p>
     * The {@code checkPackageAccess} method for class
     * {@code SecurityManager}  calls
     * {@code checkPermission} with the
     * {@code RuntimePermission("accessClassInPackage."+ pkgname)}
     * permission.
     *
     * @param      pkgname   the package name.
     * @exception  SecurityException  if the caller does not have
     *             permission to access the specified package.
     * @see        java.lang.ClassLoader#loadClass(java.lang.String, boolean)
     */
    public void checkPackageAccess(final String pkgname) {

        // first see if the VM-wide policy allows access to this package
        super.checkPackageAccess(pkgname);

        // now check the list of restricted packages
        for (Iterator<String> iter = restrictedPackages.iterator(); iter.hasNext();)
        {
            String pkg = iter.next();

            // Prevent matching "sun" and "sunir" even if they
            // starts with similar beginning characters
            //
            if (pkgname.equals(pkg) || pkgname.startsWith(pkg + "."))
            {
                checkPermission(new java.lang.RuntimePermission
                            ("accessClassInPackage." + pkgname));
            }
        }
    }

    /**
     * Returns the thread group of the applet. We consult the classloader
     * if there is one.
     */
    public ThreadGroup getThreadGroup() {
        /* If any applet code is on the execution stack, we return
           that applet's ThreadGroup.  Otherwise, we use the default
           behavior. */
        AppletClassLoader appletLoader = currentAppletClassLoader();
        ThreadGroup loaderGroup = (appletLoader == null) ? null
                                          : appletLoader.getThreadGroup();
        if (loaderGroup != null) {
            return loaderGroup;
        } else {
            return super.getThreadGroup();
        }
    } // getThreadGroup()

    /**
      * Get the AppContext corresponding to the current context.
      * The default implementation returns null, but this method
      * may be overridden by various SecurityManagers
      * (e.g. AppletSecurity) to index AppContext objects by the
      * calling context.
      *
      * @return  the AppContext corresponding to the current context.
      * @see     sun.awt.AppContext
      * @see     java.lang.SecurityManager
      * @since   1.2.1
      */
    public AppContext getAppContext() {
        AppletClassLoader appletLoader = currentAppletClassLoader();

        if (appletLoader == null) {
            return null;
        } else {
            AppContext context =  appletLoader.getAppContext();

            // context == null when some thread in applet thread group
            // has not been destroyed in AppContext.dispose()
            if (context == null) {
                throw new SecurityException("Applet classloader has invalid AppContext");
            }

            return context;
        }
    }

} // class AppletSecurity