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