summaryrefslogtreecommitdiffstats
path: root/security-kernel/src
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:28:47 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:28:47 -0800
commitadc854b798c1cfe3bfd4c27d68d5cee38ca617da (patch)
tree6aed8b4923ca428942cbaa7e848d50237a3d31e0 /security-kernel/src
parent1c0fed63c71ddb230f3b304aac12caffbedf2f21 (diff)
downloadlibcore-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.java284
-rw-r--r--security-kernel/src/main/java/java/security/AccessController.java374
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);
+ }
+}