• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 /*
18  * Copyright (C) 2008 The Android Open Source Project
19  *
20  * Licensed under the Apache License, Version 2.0 (the "License");
21  * you may not use this file except in compliance with the License.
22  * You may obtain a copy of the License at
23  *
24  *      http://www.apache.org/licenses/LICENSE-2.0
25  *
26  * Unless required by applicable law or agreed to in writing, software
27  * distributed under the License is distributed on an "AS IS" BASIS,
28  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29  * See the License for the specific language governing permissions and
30  * limitations under the License.
31  */
32 
33 package java.security;
34 
35 import java.util.ArrayList;
36 import java.util.WeakHashMap;
37 
38 import org.apache.harmony.security.fortress.SecurityUtils;
39 
40 /**
41  * {@code AccessController} provides static methods to perform access control
42  * checks and privileged operations.
43  */
44 public final class AccessController {
45 
AccessController()46     private AccessController() {
47         throw new Error("statics only.");
48     }
49 
50     /**
51      * A map used to store a mapping between a given Thread and
52      * AccessControllerContext-s used in successive calls of doPrivileged(). A
53      * WeakHashMap is used to allow automagical wiping of the dead threads from
54      * the map. The thread (normally Thread.currentThread()) is used as a key
55      * for the map, and a value is ArrayList where all AccessControlContext-s
56      * are stored.
57      * ((ArrayList)contexts.get(Thread.currentThread())).lastElement() - is
58      * reference to the latest context passed to the doPrivileged() call.
59      */
60     private static final WeakHashMap<Thread, ArrayList<AccessControlContext>> contexts = new WeakHashMap<Thread, ArrayList<AccessControlContext>>();
61 
62     /**
63      * Returns the result of executing the specified privileged action. Only the
64      * {@code ProtectionDomain} of the direct caller of this method and the
65      * {@code ProtectionDomain}s of all subsequent classes in the call chain are
66      * checked to be granted the necessary permission if access checks are
67      * performed.
68      * <p>
69      * If an instance of {@code RuntimeException} is thrown during the execution
70      * of the {@code PrivilegedAction#run()} method of the given action, it will
71      * be propagated through this method.
72      *
73      * @param action
74      *            the action to be executed with privileges
75      * @return the result of executing the privileged action
76      * @throws NullPointerException
77      *             if the specified action is {@code null}
78      * @since Android 1.0
79      */
doPrivileged(PrivilegedAction<T> action)80     public static <T> T doPrivileged(PrivilegedAction<T> action) {
81         if (action == null) {
82             throw new NullPointerException("action can not be null");
83         }
84         return doPrivilegedImpl(action, null);
85     }
86 
87     /**
88      * Returns the result of executing the specified privileged action. The
89      * {@code ProtectionDomain} of the direct caller of this method, the {@code
90      * ProtectionDomain}s of all subsequent classes in the call chain and all
91      * {@code ProtectionDomain}s of the given context are checked to be granted
92      * the necessary permission if access checks are performed.
93      * <p>
94      * If an instance of {@code RuntimeException} is thrown during the execution
95      * of the {@code PrivilegedAction#run()} method of the given action, it will
96      * be propagated through this method.
97      *
98      * @param action
99      *            the action to be executed with privileges
100      * @param context
101      *            the {@code AccessControlContext} whose protection domains are
102      *            checked additionally
103      * @return the result of executing the privileged action
104      * @throws NullPointerException
105      *             if the specified action is {@code null}
106      * @since Android 1.0
107      */
doPrivileged(PrivilegedAction<T> action, AccessControlContext context)108     public static <T> T doPrivileged(PrivilegedAction<T> action,
109             AccessControlContext context) {
110         if (action == null) {
111             throw new NullPointerException("action can not be null");
112         }
113         return doPrivilegedImpl(action, context);
114     }
115 
116     /**
117      * Returns the result of executing the specified privileged action. Only the
118      * {@code ProtectionDomain} of the direct caller of this method and the
119      * {@code ProtectionDomain}s of all subsequent classes in the call chain are
120      * checked to be granted the necessary permission if access checks are
121      * performed.
122      * <p>
123      * If a checked exception is thrown by the action's run method, it will be
124      * wrapped and propagated through this method.
125      * <p>
126      * If an instance of {@code RuntimeException} is thrown during the execution
127      * of the {@code PrivilegedAction#run()} method of the given action, it will
128      * be propagated through this method.
129      *
130      * @param action
131      *            the action to be executed with privileges
132      * @return the result of executing the privileged action
133      * @throws PrivilegedActionException
134      *             if the action's run method throws any checked exception
135      * @throws NullPointerException
136      *             if the specified action is {@code null}
137      * @since Android 1.0
138      */
doPrivileged(PrivilegedExceptionAction<T> action)139     public static <T> T doPrivileged(PrivilegedExceptionAction<T> action)
140             throws PrivilegedActionException {
141         if (action == null) {
142             throw new NullPointerException("action can not be null");
143         }
144         return doPrivilegedImpl(action, null);
145     }
146 
147     /**
148      * Returns the result of executing the specified privileged action. The
149      * {@code ProtectionDomain} of the direct caller of this method, the {@code
150      * ProtectionDomain}s of all subsequent classes in the call chain and all
151      * {@code ProtectionDomain}s of the given context are checked to be granted
152      * the necessary permission if access checks are performed.
153      * <p>
154      * If a checked exception is thrown by the action's run method, it will be
155      * wrapped and propagated through this method.
156      * <p>
157      * If an instance of {@code RuntimeException} is thrown during the execution
158      * of the {@code PrivilegedAction#run()} method of the given action, it will
159      * be propagated through this method.
160      *
161      * @param action
162      *            the action to be executed with privileges
163      * @param context
164      *            the {@code AccessControlContext} whose protection domains are
165      *            checked additionally
166      * @return the result of executing the privileged action
167      * @throws PrivilegedActionException
168      *             if the action's run method throws any checked exception
169      * @throws NullPointerException
170      *             if the specified action is {@code null}
171      * @since Android 1.0
172      */
doPrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context)173     public static <T> T doPrivileged(PrivilegedExceptionAction<T> action,
174             AccessControlContext context) throws PrivilegedActionException {
175         if (action == null) {
176             throw new NullPointerException("action can not be null");
177         }
178         return doPrivilegedImpl(action, context);
179     }
180 
181     /**
182      * The real implementation of doPrivileged() method. It pushes the passed
183      * context into this thread's contexts stack, and then invokes
184      * <code>action.run()</code>. The pushed context is then investigated in the
185      * {@link #getContext()} which is called in the {@link #checkPermission}.
186      */
doPrivilegedImpl(PrivilegedExceptionAction<T> action, AccessControlContext context)187     private static <T> T doPrivilegedImpl(PrivilegedExceptionAction<T> action,
188             AccessControlContext context) throws PrivilegedActionException {
189 
190         Thread currThread = Thread.currentThread();
191 
192         ArrayList<AccessControlContext> a = null;
193         try {
194             // currThread==null means that VM warm up is in progress
195             if (currThread != null && contexts != null) {
196                 synchronized (contexts) {
197                     a = contexts.get(currThread);
198                     if (a == null) {
199                         a = new ArrayList<AccessControlContext>();
200                         contexts.put(currThread, a);
201                     }
202                 }
203                 a.add(context);
204             }
205             return action.run();
206 
207         } catch (Exception ex) {
208             // Errors automagically go through - they are not catched by this
209             // block
210 
211             // Unchecked exceptions must pass through without modification
212             if (ex instanceof RuntimeException) {
213                 throw (RuntimeException) ex;
214             }
215 
216             // All other (==checked) exceptions get wrapped
217             throw new PrivilegedActionException(ex);
218         } finally {
219             if (currThread != null) {
220                 // No need to sync() here, as each given 'a' will be accessed
221                 // only from one Thread. 'contexts' still need sync() however,
222                 // as it's accessed from different threads simultaneously
223                 if (a != null) {
224                     // it seems I will never have here [v.size() == 0]
225                     a.remove(a.size() - 1);
226                 }
227             }
228         }
229     }
230 
231     /**
232      * The real implementation of appropriate doPrivileged() method.<br>
233      * It pushes the passed context into this thread's stack of contexts and
234      * then invokes <code>action.run()</code>.<br>
235      * The pushed context is then investigated in the {@link #getContext()}
236      * which is called in the {@link #checkPermission}.
237      */
doPrivilegedImpl(PrivilegedAction<T> action, AccessControlContext context)238     private static <T> T doPrivilegedImpl(PrivilegedAction<T> action,
239             AccessControlContext context) {
240 
241         Thread currThread = Thread.currentThread();
242 
243         if (currThread == null || contexts == null) {
244             // Big boom time - VM is starting... No need to check permissions:
245             // 1st, I do believe there is no malicious code available here for
246             // this moment
247             // 2d, I cant use currentThread() as a key anyway - when it will
248             // turn into the real Thread, I'll be unable to retrieve the value
249             // stored with 'currThread==null' as a key.
250             return action.run();
251         }
252 
253         ArrayList<AccessControlContext> a = null;
254         try {
255             synchronized (contexts) {
256                 a = contexts.get(currThread);
257                 if (a == null) {
258                     a = new ArrayList<AccessControlContext>();
259                     contexts.put(currThread, a);
260                 }
261             }
262             a.add(context);
263 
264             return action.run();
265 
266         } finally {
267             // No need to sync() here, as each given 'a' will be accessed
268             // only from one Thread. 'contexts' still need sync() however,
269             // as it's accessed from different threads simultaneously
270             if (a != null) {
271                 a.remove(a.size() - 1);
272             }
273         }
274     }
275 
276     /**
277      * Checks the specified permission against the vm's current security policy.
278      * The check is performed in the context of the current thread. This method
279      * returns silently if the permission is granted, otherwise an {@code
280      * AccessControlException} is thrown.
281      * <p>
282      * A permission is considered granted if every {@link ProtectionDomain} in
283      * the current execution context has been granted the specified permission.
284      * If privileged operations are on the execution context, only the {@code
285      * ProtectionDomain}s from the last privileged operation are taken into
286      * account.
287      * <p>
288      * This method delegates the permission check to
289      * {@link AccessControlContext#checkPermission(Permission)} on the current
290      * callers' context obtained by {@link #getContext()}.
291      *
292      * @param perm
293      *            the permission to check against the policy
294      * @throws AccessControlException
295      *             if the specified permission is not granted
296      * @throws NullPointerException
297      *             if the specified permission is {@code null}
298      * @see AccessControlContext#checkPermission(Permission)
299      *
300      * @since Android 1.0
301      */
checkPermission(Permission perm)302     public static void checkPermission(Permission perm)
303             throws AccessControlException {
304         if (perm == null) {
305             throw new NullPointerException("permission can not be null");
306         }
307 
308         getContext().checkPermission(perm);
309     }
310 
311     /**
312      * Returns array of ProtectionDomains from the classes residing on the stack
313      * of the current thread, up to and including the caller of the nearest
314      * privileged frame. Reflection frames are skipped. The returned array is
315      * never null and never contains null elements, meaning that bootstrap
316      * classes are effectively ignored.
317      */
getStackDomains()318     private static native ProtectionDomain[] getStackDomains();
319 
320     /**
321      * Returns the {@code AccessControlContext} for the current {@code Thread}
322      * including the inherited access control context of the thread that spawned
323      * the current thread (recursively).
324      * <p>
325      * The returned context may be used to perform access checks at a later
326      * point in time, possibly by another thread.
327      *
328      * @return the {@code AccessControlContext} for the current {@code Thread}
329      * @see Thread#currentThread
330      * @since Android 1.0
331      */
getContext()332     public static AccessControlContext getContext() {
333 
334         // duplicates (if any) will be removed in ACC constructor
335         ProtectionDomain[] stack = getStackDomains();
336 
337         Thread currThread = Thread.currentThread();
338         if (currThread == null || contexts == null) {
339             // Big boo time. No need to check anything ?
340             return new AccessControlContext(stack);
341         }
342 
343         ArrayList<AccessControlContext> threadContexts;
344         synchronized (contexts) {
345             threadContexts = contexts.get(currThread);
346         }
347 
348         AccessControlContext that;
349         if ((threadContexts == null) || (threadContexts.size() == 0)) {
350             // We were not in doPrivileged method, so
351             // have inherited context here
352             that = SecurityUtils.getContext(currThread);
353         } else {
354             // We were in doPrivileged method, so
355             // Use context passed to the doPrivileged()
356             that = threadContexts.get(threadContexts.size() - 1);
357         }
358 
359         if (that != null && that.combiner != null) {
360             ProtectionDomain[] assigned = null;
361             if (that.context != null && that.context.length != 0) {
362                 assigned = new ProtectionDomain[that.context.length];
363                 System.arraycopy(that.context, 0, assigned, 0, assigned.length);
364             }
365             ProtectionDomain[] allpds = that.combiner.combine(stack, assigned);
366             if (allpds == null) {
367                 allpds = new ProtectionDomain[0];
368             }
369             return new AccessControlContext(allpds, that.combiner);
370         }
371 
372         return new AccessControlContext(stack, that);
373     }
374 }
375