/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.security; import java.util.ArrayList; import java.util.List; import java.util.WeakHashMap; import org.apache.harmony.security.fortress.SecurityUtils; /** * {@code AccessController} provides static methods to perform access control * checks and privileged operations. */ public final class AccessController { private AccessController() { throw new Error("statics only."); } /** * A map used to store a mapping between a given Thread and * AccessControllerContext-s used in successive calls of doPrivileged(). A * WeakHashMap is used to allow automatic wiping of the dead threads from * the map. The thread (normally Thread.currentThread()) is used as a key * for the map, and a value is ArrayList where all AccessControlContext-s * are stored. * ((ArrayList)contexts.get(Thread.currentThread())).lastElement() - is * reference to the latest context passed to the doPrivileged() call. * * TODO: ThreadLocal? */ private static final WeakHashMap> contexts = new WeakHashMap>(); /** * Returns the result of executing the specified privileged action. Only the * {@code ProtectionDomain} of the direct caller of this method and the * {@code ProtectionDomain}s of all subsequent classes in the call chain are * checked to be granted the necessary permission if access checks are * performed. *

* If an instance of {@code RuntimeException} is thrown during the execution * of the {@code PrivilegedAction#run()} method of the given action, it will * be propagated through this method. * * @param action * the action to be executed with privileges * @return the result of executing the privileged action * @throws NullPointerException * if the specified action is {@code null} */ public static T doPrivileged(PrivilegedAction action) { if (action == null) { throw new NullPointerException("action == null"); } return doPrivileged(action, null); } /** * Returns the result of executing the specified privileged action. The * {@code ProtectionDomain} of the direct caller of this method, the {@code * ProtectionDomain}s of all subsequent classes in the call chain and all * {@code ProtectionDomain}s of the given context are checked to be granted * the necessary permission if access checks are performed. *

* If an instance of {@code RuntimeException} is thrown during the execution * of the {@code PrivilegedAction#run()} method of the given action, it will * be propagated through this method. * * @param action * the action to be executed with privileges * @param context * the {@code AccessControlContext} whose protection domains are * checked additionally * @return the result of executing the privileged action * @throws NullPointerException * if the specified action is {@code null} */ public static T doPrivileged(PrivilegedAction action, AccessControlContext context) { if (action == null) { throw new NullPointerException("action == null"); } List contextsStack = contextsForThread(); contextsStack.add(context); try { return action.run(); } finally { contextsStack.remove(contextsStack.size() - 1); } } /** * Returns the result of executing the specified privileged action. Only the * {@code ProtectionDomain} of the direct caller of this method and the * {@code ProtectionDomain}s of all subsequent classes in the call chain are * checked to be granted the necessary permission if access checks are * performed. *

* If a checked exception is thrown by the action's run method, it will be * wrapped and propagated through this method. *

* If an instance of {@code RuntimeException} is thrown during the execution * of the {@code PrivilegedAction#run()} method of the given action, it will * be propagated through this method. * * @param action * the action to be executed with privileges * @return the result of executing the privileged action * @throws PrivilegedActionException * if the action's run method throws any checked exception * @throws NullPointerException * if the specified action is {@code null} */ public static T doPrivileged(PrivilegedExceptionAction action) throws PrivilegedActionException { return doPrivileged(action, null); } /** * Returns the result of executing the specified privileged action. The * {@code ProtectionDomain} of the direct caller of this method, the {@code * ProtectionDomain}s of all subsequent classes in the call chain and all * {@code ProtectionDomain}s of the given context are checked to be granted * the necessary permission if access checks are performed. *

* If a checked exception is thrown by the action's run method, it will be * wrapped and propagated through this method. *

* If an instance of {@code RuntimeException} is thrown during the execution * of the {@code PrivilegedAction#run()} method of the given action, it will * be propagated through this method. * * @param action * the action to be executed with privileges * @param context * the {@code AccessControlContext} whose protection domains are * checked additionally * @return the result of executing the privileged action * @throws PrivilegedActionException * if the action's run method throws any checked exception * @throws NullPointerException * if the specified action is {@code null} */ public static T doPrivileged(PrivilegedExceptionAction action, AccessControlContext context) throws PrivilegedActionException { if (action == null) { throw new NullPointerException("action == null"); } List contextsStack = contextsForThread(); contextsStack.add(context); try { return action.run(); } catch (RuntimeException e) { throw e; // so we don't wrap RuntimeExceptions with PrivilegedActionException } catch (Exception e) { throw new PrivilegedActionException(e); } finally { contextsStack.remove(contextsStack.size() - 1); } } public static T doPrivilegedWithCombiner(PrivilegedAction action) { return doPrivileged(action, newContextSameDomainCombiner()); } public static T doPrivilegedWithCombiner(PrivilegedExceptionAction action) throws PrivilegedActionException { return doPrivileged(action, newContextSameDomainCombiner()); } private static AccessControlContext newContextSameDomainCombiner() { List contextsStack = contextsForThread(); DomainCombiner domainCombiner = contextsStack.isEmpty() ? null : contextsStack.get(contextsStack.size() - 1).getDomainCombiner(); return new AccessControlContext(new ProtectionDomain[0], domainCombiner); } private static List contextsForThread() { Thread currThread = Thread.currentThread(); /* * Thread.currentThread() is null when Thread.class is being initialized, and contexts is * null when AccessController.class is still being initialized. In either case, return an * empty list so callers need not worry. */ if (currThread == null || contexts == null) { return new ArrayList(); } synchronized (contexts) { ArrayList result = contexts.get(currThread); if (result == null) { result = new ArrayList(); contexts.put(currThread, result); } return result; } } /** * Checks the specified permission against the VM's current security policy. * The check is performed in the context of the current thread. This method * returns silently if the permission is granted, otherwise an {@code * AccessControlException} is thrown. *

* A permission is considered granted if every {@link ProtectionDomain} in * the current execution context has been granted the specified permission. * If privileged operations are on the execution context, only the {@code * ProtectionDomain}s from the last privileged operation are taken into * account. *

* This method delegates the permission check to * {@link AccessControlContext#checkPermission(Permission)} on the current * callers' context obtained by {@link #getContext()}. * * @param permission * the permission to check against the policy * @throws AccessControlException * if the specified permission is not granted * @throws NullPointerException * if the specified permission is {@code null} * @see AccessControlContext#checkPermission(Permission) * */ public static void checkPermission(Permission permission) throws AccessControlException { if (permission == null) { throw new NullPointerException("permission == null"); } getContext().checkPermission(permission); } /** * Returns array of ProtectionDomains from the classes residing on the stack * of the current thread, up to and including the caller of the nearest * privileged frame. Reflection frames are skipped. The returned array is * never null and never contains null elements, meaning that bootstrap * classes are effectively ignored. */ private static native ProtectionDomain[] getStackDomains(); /** * Returns the {@code AccessControlContext} for the current {@code Thread} * including the inherited access control context of the thread that spawned * the current thread (recursively). *

* The returned context may be used to perform access checks at a later * point in time, possibly by another thread. * * @return the {@code AccessControlContext} for the current {@code Thread} * @see Thread#currentThread */ public static AccessControlContext getContext() { // duplicates (if any) will be removed in ACC constructor ProtectionDomain[] stack = getStackDomains(); Thread currentThread = Thread.currentThread(); if (currentThread == null || AccessController.contexts == null) { // Big boo time. No need to check anything ? return new AccessControlContext(stack); } List threadContexts = contextsForThread(); // if we're in a doPrivileged method, use its context. AccessControlContext that = threadContexts.isEmpty() ? SecurityUtils.getContext(currentThread) : threadContexts.get(threadContexts.size() - 1); if (that != null && that.combiner != null) { ProtectionDomain[] assigned = null; if (that.context != null && that.context.length != 0) { assigned = new ProtectionDomain[that.context.length]; System.arraycopy(that.context, 0, assigned, 0, assigned.length); } ProtectionDomain[] protectionDomains = that.combiner.combine(stack, assigned); if (protectionDomains == null) { protectionDomains = new ProtectionDomain[0]; } return new AccessControlContext(protectionDomains, that.combiner); } return new AccessControlContext(stack, that); } }