blob: d395592d3e824abb826f75339ff79d4b666f93d1 [file] [log] [blame]
/*
* 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<Thread, ArrayList<AccessControlContext>> contexts = new WeakHashMap<Thread, ArrayList<AccessControlContext>>();
/**
* 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.
* <p>
* 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> T doPrivileged(PrivilegedAction<T> 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.
* <p>
* 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> T doPrivileged(PrivilegedAction<T> action,
AccessControlContext context) {
if (action == null) {
throw new NullPointerException("action == null");
}
List<AccessControlContext> 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.
* <p>
* If a checked exception is thrown by the action's run method, it will be
* wrapped and propagated through this method.
* <p>
* 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> T doPrivileged(PrivilegedExceptionAction<T> 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.
* <p>
* If a checked exception is thrown by the action's run method, it will be
* wrapped and propagated through this method.
* <p>
* 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> T doPrivileged(PrivilegedExceptionAction<T> action,
AccessControlContext context) throws PrivilegedActionException {
if (action == null) {
throw new NullPointerException("action == null");
}
List<AccessControlContext> 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> T doPrivilegedWithCombiner(PrivilegedAction<T> action) {
return doPrivileged(action, newContextSameDomainCombiner());
}
public static <T> T doPrivilegedWithCombiner(PrivilegedExceptionAction<T> action)
throws PrivilegedActionException {
return doPrivileged(action, newContextSameDomainCombiner());
}
private static AccessControlContext newContextSameDomainCombiner() {
List<AccessControlContext> contextsStack = contextsForThread();
DomainCombiner domainCombiner = contextsStack.isEmpty()
? null
: contextsStack.get(contextsStack.size() - 1).getDomainCombiner();
return new AccessControlContext(new ProtectionDomain[0], domainCombiner);
}
private static List<AccessControlContext> 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<AccessControlContext>();
}
synchronized (contexts) {
ArrayList<AccessControlContext> result = contexts.get(currThread);
if (result == null) {
result = new ArrayList<AccessControlContext>();
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.
* <p>
* 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.
* <p>
* 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).
* <p>
* 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<AccessControlContext> 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);
}
}