diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:28:47 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:28:47 -0800 |
commit | adc854b798c1cfe3bfd4c27d68d5cee38ca617da (patch) | |
tree | 6aed8b4923ca428942cbaa7e848d50237a3d31e0 /security-kernel/src | |
parent | 1c0fed63c71ddb230f3b304aac12caffbedf2f21 (diff) | |
download | libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.zip libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.tar.gz libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'security-kernel/src')
-rw-r--r-- | security-kernel/src/main/java/java/security/AccessControlContext.java | 284 | ||||
-rw-r--r-- | security-kernel/src/main/java/java/security/AccessController.java | 374 |
2 files changed, 658 insertions, 0 deletions
diff --git a/security-kernel/src/main/java/java/security/AccessControlContext.java b/security-kernel/src/main/java/java/security/AccessControlContext.java new file mode 100644 index 0000000..597cc44 --- /dev/null +++ b/security-kernel/src/main/java/java/security/AccessControlContext.java @@ -0,0 +1,284 @@ +/* + * 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 org.apache.harmony.security.fortress.PolicyUtils; + +import java.util.ArrayList; + +/** + * {@code AccessControlContext} encapsulates the {@code ProtectionDomain}s on + * which access control decisions are based. + */ +public final class AccessControlContext { + + // List of ProtectionDomains wrapped by the AccessControlContext + // It has the following characteristics: + // - 'context' can not be null + // - never contains null(s) + // - all elements are unique (no dups) + ProtectionDomain[] context; + + DomainCombiner combiner; + + // An AccessControlContext inherited by the current thread from its parent + private AccessControlContext inherited; + + /** + * Constructs a new instance of {@code AccessControlContext} with the + * specified {@code AccessControlContext} and {@code DomainCombiner}. + * <p> + * If a {@code SecurityManager} is installed, code calling this constructor + * need the {@code SecurityPermission} {@code createAccessControlContext} to + * be granted, otherwise a {@code SecurityException} will be thrown. + * + * @param acc + * the {@code AccessControlContext} related to the given {@code + * DomainCombiner} + * @param combiner + * the {@code DomainCombiner} related to the given {@code + * AccessControlContext} + * @throws SecurityException + * if a {@code SecurityManager} is installed and the caller does + * not have permission to invoke this constructor + * @throws NullPointerException + * if {@code acc} is {@code null} + * @since Android 1.0 + */ + public AccessControlContext(AccessControlContext acc, + DomainCombiner combiner) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new SecurityPermission( + "createAccessControlContext")); + } + // no need to clone() here as ACC is immutable + this.context = acc.context; + this.combiner = combiner; + } + + /** + * Constructs a new instance of {@code AccessControlContext} with the + * specified array of {@code ProtectionDomain}s. + * + * @param context + * the {@code ProtectionDomain}s that are used to perform access + * checks in the context of this {@code AccessControlContext} + * @throws NullPointerException + * if {@code context} is {@code null} + * @since Android 1.0 + */ + public AccessControlContext(ProtectionDomain[] context) { + if (context == null) { + throw new NullPointerException("context can not be null"); + } + if (context.length != 0) { + // remove dup entries + ArrayList<ProtectionDomain> a = new ArrayList<ProtectionDomain>(); + for (int i = 0; i < context.length; i++) { + if (context[i] != null && !a.contains(context[i])) { + a.add(context[i]); + } + } + if (a.size() != 0) { + this.context = new ProtectionDomain[a.size()]; + a.toArray(this.context); + } + } + if (this.context == null) { + // Prevent numerous checks for 'context==null' + this.context = new ProtectionDomain[0]; + } + } + + /** + * Package-level ctor which is used in AccessController.<br> + * ProtectionDomains passed as <code>stack</code> is then passed into + * {@link #AccessControlContext(ProtectionDomain[])}, therefore:<br> + * <il> + * <li>it must not be null + * <li>duplicates will be removed + * <li>null-s will be removed + * </li> + * + * @param stack - array of ProtectionDomains + * @param inherited - inherited context, which may be null + */ + AccessControlContext(ProtectionDomain[] stack, + AccessControlContext inherited) { + this(stack); // removes dups, removes nulls, checks for stack==null + this.inherited = inherited; + } + + /** + * Package-level ctor which is used in AccessController.<br> + * ProtectionDomains passed as <code>stack</code> is then passed into + * {@link #AccessControlContext(ProtectionDomain[])}, therefore:<br> + * <il> + * <li>it must not be null + * <li>duplicates will be removed + * <li>null-s will be removed + * </li> + * + * @param stack - array of ProtectionDomains + * @param combiner - combiner + */ + AccessControlContext(ProtectionDomain[] stack, + DomainCombiner combiner) { + this(stack); // removes dups, removes nulls, checks for stack==null + this.combiner = combiner; + } + + /** + * Checks the specified permission against the vm's current security policy. + * The check is based on this {@code AccessControlContext} as opposed to the + * {@link AccessController#checkPermission(Permission)} method which + * performs access checks based on 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 + * this context has been granted the specified permission. + * <p> + * If privileged operations are on the call stack, only the {@code + * ProtectionDomain}s from the last privileged operation are taken into + * account. + * <p> + * If inherited methods are on the call stack, the protection domains of the + * declaring classes are checked, not the protection domains of the classes + * on which the method is invoked. + * + * @param perm + * 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 AccessController#checkPermission(Permission) + * @since Android 1.0 + */ + public void checkPermission(Permission perm) throws AccessControlException { + if (perm == null) { + throw new NullPointerException("Permission cannot be null"); + } + for (int i = 0; i < context.length; i++) { + if (!context[i].implies(perm)) { + throw new AccessControlException("Permission check failed " + + perm, perm); + } + } + if (inherited != null) { + inherited.checkPermission(perm); + } + } + + + /** + * Compares the specified object with this {@code AccessControlContext} for + * equality. Returns {@code true} if the specified object is also an + * instance of {@code AccessControlContext}, and the two contexts + * encapsulate the same {@code ProtectionDomain}s. The order of the {@code + * ProtectionDomain}s is ignored by this method. + * + * @param obj + * object to be compared for equality with this {@code + * AccessControlContext} + * @return {@code true} if the specified object is equal to this {@code + * AccessControlContext}, otherwise {@code false} + * @since Android 1.0 + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof AccessControlContext) { + AccessControlContext that = (AccessControlContext) obj; + if (!(PolicyUtils.matchSubset(context, that.context) && PolicyUtils + .matchSubset(that.context, context))) { + return false; + } + // BEGIN android-changed + if(combiner != null) { + return combiner.equals(that.combiner); + } + return that.combiner == null; + // END android-changed + } + return false; + } + + /** + * Returns the {@code DomainCombiner} associated with this {@code + * AccessControlContext}. + * <p> + * If a {@code SecurityManager} is installed, code calling this method needs + * the {@code SecurityPermission} {@code getDomainCombiner} to be granted, + * otherwise a {@code SecurityException} will be thrown. + * + * @return the {@code DomainCombiner} associated with this {@code + * AccessControlContext} + * @throws SecurityException + * if a {@code SecurityManager} is installed and the caller does + * not have permission to invoke this method + * @since Android 1.0 + */ + public DomainCombiner getDomainCombiner() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new SecurityPermission("getDomainCombiner")); + } + return combiner; + } + + + /** + * Returns the hash code value for this {@code AccessControlContext}. + * Returns the same hash code for {@code AccessControlContext}s that are + * equal to each other as required by the general contract of + * {@link Object#hashCode}. + * + * @return the hash code value for this {@code AccessControlContext} + * @see Object#equals(Object) + * @see AccessControlContext#equals(Object) + * @since Android 1.0 + */ + public int hashCode() { + int hash = 0; + for (int i = 0; i < context.length; i++) { + hash ^= context[i].hashCode(); + } + return hash; + } + +} diff --git a/security-kernel/src/main/java/java/security/AccessController.java b/security-kernel/src/main/java/java/security/AccessController.java new file mode 100644 index 0000000..5c5bc26 --- /dev/null +++ b/security-kernel/src/main/java/java/security/AccessController.java @@ -0,0 +1,374 @@ +/* + * 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.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 automagical 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. + */ + 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} + * @since Android 1.0 + */ + public static <T> T doPrivileged(PrivilegedAction<T> action) { + if (action == null) { + throw new NullPointerException("action can not be null"); + } + return doPrivilegedImpl(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} + * @since Android 1.0 + */ + public static <T> T doPrivileged(PrivilegedAction<T> action, + AccessControlContext context) { + if (action == null) { + throw new NullPointerException("action can not be null"); + } + return doPrivilegedImpl(action, context); + } + + /** + * 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} + * @since Android 1.0 + */ + public static <T> T doPrivileged(PrivilegedExceptionAction<T> action) + throws PrivilegedActionException { + if (action == null) { + throw new NullPointerException("action can not be null"); + } + return doPrivilegedImpl(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} + * @since Android 1.0 + */ + public static <T> T doPrivileged(PrivilegedExceptionAction<T> action, + AccessControlContext context) throws PrivilegedActionException { + if (action == null) { + throw new NullPointerException("action can not be null"); + } + return doPrivilegedImpl(action, context); + } + + /** + * The real implementation of doPrivileged() method. It pushes the passed + * context into this thread's contexts stack, and then invokes + * <code>action.run()</code>. The pushed context is then investigated in the + * {@link #getContext()} which is called in the {@link #checkPermission}. + */ + private static <T> T doPrivilegedImpl(PrivilegedExceptionAction<T> action, + AccessControlContext context) throws PrivilegedActionException { + + Thread currThread = Thread.currentThread(); + + ArrayList<AccessControlContext> a = null; + try { + // currThread==null means that VM warm up is in progress + if (currThread != null && contexts != null) { + synchronized (contexts) { + a = contexts.get(currThread); + if (a == null) { + a = new ArrayList<AccessControlContext>(); + contexts.put(currThread, a); + } + } + a.add(context); + } + return action.run(); + + } catch (Exception ex) { + // Errors automagically go through - they are not catched by this + // block + + // Unchecked exceptions must pass through without modification + if (ex instanceof RuntimeException) { + throw (RuntimeException) ex; + } + + // All other (==checked) exceptions get wrapped + throw new PrivilegedActionException(ex); + } finally { + if (currThread != null) { + // No need to sync() here, as each given 'a' will be accessed + // only from one Thread. 'contexts' still need sync() however, + // as it's accessed from different threads simultaneously + if (a != null) { + // it seems I will never have here [v.size() == 0] + a.remove(a.size() - 1); + } + } + } + } + + /** + * The real implementation of appropriate doPrivileged() method.<br> + * It pushes the passed context into this thread's stack of contexts and + * then invokes <code>action.run()</code>.<br> + * The pushed context is then investigated in the {@link #getContext()} + * which is called in the {@link #checkPermission}. + */ + private static <T> T doPrivilegedImpl(PrivilegedAction<T> action, + AccessControlContext context) { + + Thread currThread = Thread.currentThread(); + + if (currThread == null || contexts == null) { + // Big boom time - VM is starting... No need to check permissions: + // 1st, I do believe there is no malicious code available here for + // this moment + // 2d, I cant use currentThread() as a key anyway - when it will + // turn into the real Thread, I'll be unable to retrieve the value + // stored with 'currThread==null' as a key. + return action.run(); + } + + ArrayList<AccessControlContext> a = null; + try { + synchronized (contexts) { + a = contexts.get(currThread); + if (a == null) { + a = new ArrayList<AccessControlContext>(); + contexts.put(currThread, a); + } + } + a.add(context); + + return action.run(); + + } finally { + // No need to sync() here, as each given 'a' will be accessed + // only from one Thread. 'contexts' still need sync() however, + // as it's accessed from different threads simultaneously + if (a != null) { + a.remove(a.size() - 1); + } + } + } + + /** + * 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 perm + * 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) + * + * @since Android 1.0 + */ + public static void checkPermission(Permission perm) + throws AccessControlException { + if (perm == null) { + throw new NullPointerException("permission can not be null"); + } + + getContext().checkPermission(perm); + } + + /** + * 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 + * @since Android 1.0 + */ + public static AccessControlContext getContext() { + + // duplicates (if any) will be removed in ACC constructor + ProtectionDomain[] stack = getStackDomains(); + + Thread currThread = Thread.currentThread(); + if (currThread == null || contexts == null) { + // Big boo time. No need to check anything ? + return new AccessControlContext(stack); + } + + ArrayList<AccessControlContext> threadContexts; + synchronized (contexts) { + threadContexts = contexts.get(currThread); + } + + AccessControlContext that; + if ((threadContexts == null) || (threadContexts.size() == 0)) { + // We were not in doPrivileged method, so + // have inherited context here + that = SecurityUtils.getContext(currThread); + } else { + // We were in doPrivileged method, so + // Use context passed to the doPrivileged() + that = 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[] allpds = that.combiner.combine(stack, assigned); + if (allpds == null) { + allpds = new ProtectionDomain[0]; + } + return new AccessControlContext(allpds, that.combiner); + } + + return new AccessControlContext(stack, that); + } +} |