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
33package java.security;
34
35import java.util.ArrayList;
36import java.util.WeakHashMap;
37
38import org.apache.harmony.security.fortress.SecurityUtils;
39
40/**
41 * {@code AccessController} provides static methods to perform access control
42 * checks and privileged operations.
43 */
44public final class AccessController {
45
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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