blob: b8ac261f8dcf977132eeef09f2aeaf5fb5b963b9 [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.
*/
package java.security;
/**
* This class must be implemented by the vm vendor, or the reference
* implementation can be used if the documented native is implemented.
*
* Checks access to system resources. Supports marking of code as priveleged.
* Makes context snapshots to allow checking from other contexts.
*/
public final class AccessController {
static {
// Initialize vm-internal caches
initializeInternal();
}
private static native void initializeInternal();
/**
* Prevents this class from being instantiated.
*/
private AccessController() {
}
/**
* This native must be implemented to use the reference implementation of
* this class. It is used by checkPermission() and getContext(), which call
* this native with depth = 1.
*
* Returns an array of ProtectionDomain from the classes on the stack, from
* the specified depth up to the first privileged frame, or the end of the
* stack if there is not a privileged frame. The array may be larger than
* required, but must be null terminated. As bootstrap classes have all
* permissions, bootstrap class frames SHOULD be skipped. Bootstrap class
* frames MUST be skipped if the ProtectionDomain of bootstrap classes is
* null. Duplicate ProtectionDomains SHOULD be removed.
*
* The first element of the result is the AccessControlContext, which may be
* null, either from the privileged frame, or from the current Thread if
* there is not a privileged frame.
*
* A privileged frame is any frame running one of the following methods:
*
* <code><ul>
* <li>java/security/AccessController.doPrivileged(Ljava/security/PrivilegedAction;)Ljava/lang/Object;</li>
* <li>java/security/AccessController.doPrivileged(Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;</li>
* <li>java/security/AccessController.doPrivileged(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;</li>
* <li>java/security/AccessController.doPrivileged(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;</li>
* </ul></code>
*
* @param depth
* The stack depth at which to start. Depth 0 is the current
* frame (the caller of this native).
*
* @return an Object[] where the first element is AccessControlContext, and
* the other elements are ProtectionsDomain.
*/
private static native Object[] getProtectionDomains(int depth);
/**
* Checks whether the running program is allowed to access the resource
* being guarded by the given Permission argument.
*
*
* @param perm
* the permission to check
* @exception AccessControlException
* if access is not allowed.
*/
public static void checkPermission(Permission perm)
throws AccessControlException {
if (perm == null)
throw new NullPointerException();
Object[] domains = getProtectionDomains(1);
AccessControlContext acc = (AccessControlContext) domains[0];
ProtectionDomain[] pDomains = null;
if (acc != null && acc.domainCombiner != null) {
pDomains = acc.domainCombiner.combine(toArrayOfProtectionDomains(
domains, null), acc.domainsArray);
} else {
pDomains = toArrayOfProtectionDomains(domains, acc);
}
for (int i = 0, length = pDomains.length; i < length; i++) {
if (!pDomains[i].implies(perm)) {
throw new AccessControlException("Access Denied " + perm, perm); //$NON-NLS-1$
}
}
}
/**
* Used to keep the context live during doPrivileged().
*
* @see #doPrivileged(PrivilegedAction, AccessControlContext)
*/
private static void keepalive(AccessControlContext context) {
}
/**
* Answers the access controller context of the current thread, including
* the inherited ones. It basically retrieves all the protection domains
* from the calling stack and creates an <code>AccessControlContext</code>
* with them.
*
* @return the access control context of the current thread
* @see AccessControlContext
*/
public static AccessControlContext getContext() {
Object[] domains = getProtectionDomains(1);
AccessControlContext acc = (AccessControlContext) domains[0];
ProtectionDomain[] pDomains = null;
if (acc != null && acc.domainCombiner != null) {
pDomains = acc.domainCombiner.combine(toArrayOfProtectionDomains(
domains, null), acc.domainsArray);
AccessControlContext result = new AccessControlContext(pDomains,
false);
result.domainCombiner = acc.domainCombiner;
return result;
}
return new AccessControlContext(
toArrayOfProtectionDomains(domains, acc), false);
}
private static ProtectionDomain[] toArrayOfProtectionDomains(
Object[] domains, AccessControlContext acc) {
int len = 0, size = domains.length - 1;
int extra = acc == null ? 0 : acc.domainsArray.length;
ProtectionDomain[] answer = new ProtectionDomain[size + extra];
for (int i = 1; i <= size; i++) {
boolean found = false;
if ((answer[len] = (ProtectionDomain) domains[i]) == null)
break;
if (acc != null) {
for (int j = 0; j < acc.domainsArray.length; j++) {
if (answer[len] == acc.domainsArray[j]) {
found = true;
break;
}
}
}
if (!found)
len++;
}
if (len == 0 && acc != null)
return acc.domainsArray;
else if (len < size) {
ProtectionDomain[] copy = new ProtectionDomain[len + extra];
System.arraycopy(answer, 0, copy, 0, len);
answer = copy;
}
if (acc != null)
System.arraycopy(acc.domainsArray, 0, answer, len,
acc.domainsArray.length);
return answer;
}
/**
* Performs the privileged action specified by <code>action</code>.
*
* When permission checks are made, if the permission has been granted by
* all frames below and including the one representing the call to this
* method, then the permission is granted. In otherwords, the check stops
* here.
*
* Any unchecked exception generated by this method will propagate up the
* chain.
*
* @param action
* the action being performed
* @param <T>
* the return type for the privileged action
* @return the result of evaluating the action
*
* @see #doPrivileged(PrivilegedAction)
*/
public static <T> T doPrivileged(PrivilegedAction<T> action) {
return action.run();
}
/**
* Performs the privileged action specified by <code>action</code>.
*
* When permission checks are made, if the permission has been granted by
* all frames below and including the one representing the call to this
* method, then the permission is granted iff it is granted by the
* AccessControlContext <code>context</code>. In otherwords, no more
* checking of the current stack is performed. Instead, the passed in
* context is checked.
*
* Any unchecked exception generated by this method will propagate up the
* chain.
*
* @param action
* the action being performed
* @param <T>
* the return type for the privileged action
* @param context
* the context being checked for the privileged action
* @return the result of evaluating the action
*
* @see #doPrivileged(PrivilegedAction)
*/
public static <T> T doPrivileged(PrivilegedAction<T> action,
AccessControlContext context) {
T result = action.run();
keepalive(context);
return result;
}
/**
* Performs the privileged action specified by <code>action</code>.
*
* When permission checks are made, if the permission has been granted by
* all frames below and including the one representing the call to this
* method, then the permission is granted. In otherwords, the check stops
* here.
*
* Any unchecked exception generated by this method will propagate up the
* chain. However, checked exceptions will be caught an re-thrown as
* PrivilegedActionExceptions.
*
* @param action
* the action being performed
* @param <T>
* the return type for the privileged action
* @return the result of evaluating the action
* @throws PrivilegedActionException
* if a checked exception was thrown
* @see #doPrivileged(PrivilegedAction)
*/
public static <T> T doPrivileged(PrivilegedExceptionAction<T> action)
throws PrivilegedActionException {
try {
return action.run();
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new PrivilegedActionException(ex);
}
}
/**
* Performs the privileged action specified by <code>action</code>.
*
* When permission checks are made, if the permission has been granted by
* all frames below and including the one representing the call to this
* method, then the permission is granted iff it is granted by the
* AccessControlContext <code>context</code>. In otherwords, no more
* checking of the current stack is performed. Instead, the passed in
* context is checked.
*
* Any unchecked exception generated by this method will propagate up the
* chain. However, checked exceptions will be caught an re-thrown as
* PrivilegedActionExceptions
*
* @param action
* the action being performed
* @param <T>
* the return type for the privileged action
* @param context
* the context being checked for the privileged action
* @return the result of evaluating the action
* @throws PrivilegedActionException
* if a checked exception was thrown
*
* @see #doPrivileged(PrivilegedAction)
*/
public static <T> T doPrivileged(PrivilegedExceptionAction<T> action,
AccessControlContext context) throws PrivilegedActionException {
try {
T result = action.run();
keepalive(context);
return result;
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new PrivilegedActionException(ex);
}
}
}