1 /* 2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.security; 27 28 import java.lang.ref.Reference; 29 import java.lang.ref.ReferenceQueue; 30 import java.lang.ref.WeakReference; 31 import java.util.ArrayList; 32 import java.util.Enumeration; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.WeakHashMap; 36 import java.util.concurrent.ConcurrentHashMap; 37 import sun.misc.JavaSecurityAccess; 38 import sun.misc.JavaSecurityProtectionDomainAccess; 39 import static sun.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache; 40 import sun.misc.SharedSecrets; 41 import sun.security.util.Debug; 42 import sun.security.util.SecurityConstants; 43 44 /** 45 * The ProtectionDomain class encapsulates the characteristics of a domain, 46 * which encloses a set of classes whose instances are granted a set 47 * of permissions when being executed on behalf of a given set of Principals. 48 * <p> 49 * A static set of permissions can be bound to a ProtectionDomain when it is 50 * constructed; such permissions are granted to the domain regardless of the 51 * Policy in force. However, to support dynamic security policies, a 52 * ProtectionDomain can also be constructed such that it is dynamically 53 * mapped to a set of permissions by the current Policy whenever a permission 54 * is checked. 55 * 56 * @author Li Gong 57 * @author Roland Schemers 58 * @author Gary Ellison 59 */ 60 61 public class ProtectionDomain { 62 63 private static class JavaSecurityAccessImpl implements JavaSecurityAccess { 64 65 private JavaSecurityAccessImpl() { 66 } 67 68 @Override 69 public <T> T doIntersectionPrivilege( 70 PrivilegedAction<T> action, 71 final AccessControlContext stack, 72 final AccessControlContext context) { 73 if (action == null) { 74 throw new NullPointerException(); 75 } 76 77 return AccessController.doPrivileged( 78 action, 79 getCombinedACC(context, stack) 80 ); 81 } 82 83 @Override 84 public <T> T doIntersectionPrivilege( 85 PrivilegedAction<T> action, 86 AccessControlContext context) { 87 return doIntersectionPrivilege(action, 88 AccessController.getContext(), context); 89 } 90 91 private static AccessControlContext getCombinedACC( 92 AccessControlContext context, AccessControlContext stack) { 93 AccessControlContext acc = 94 new AccessControlContext(context, stack.getCombiner(), true); 95 96 return new AccessControlContext(stack.getContext(), acc).optimize(); 97 } 98 } 99 100 static { 101 // setup SharedSecrets to allow access to doIntersectionPrivilege 102 // methods and ProtectionDomain cache 103 SharedSecrets.setJavaSecurityAccess(new JavaSecurityAccessImpl()); 104 SharedSecrets.setJavaSecurityProtectionDomainAccess( 105 new JavaSecurityProtectionDomainAccess() { 106 @Override 107 public ProtectionDomainCache getProtectionDomainCache() { 108 return new PDCache(); 109 } 110 }); 111 } 112 113 /** 114 * Used for storing ProtectionDomains as keys in a Map. 115 */ 116 static final class Key {} 117 118 /* CodeSource */ 119 private CodeSource codesource ; 120 121 /* ClassLoader the protection domain was consed from */ 122 private ClassLoader classloader; 123 124 /* Principals running-as within this protection domain */ 125 private Principal[] principals; 126 127 /* the rights this protection domain is granted */ 128 private PermissionCollection permissions; 129 130 /* if the permissions object has AllPermission */ 131 private boolean hasAllPerm = false; 132 133 /* the PermissionCollection is static (pre 1.4 constructor) 134 or dynamic (via a policy refresh) */ 135 private boolean staticPermissions; 136 137 /* 138 * An object used as a key when the ProtectionDomain is stored in a Map. 139 */ 140 final Key key = new Key(); 141 142 private static final Debug debug = Debug.getInstance("domain"); 143 144 /** 145 * Creates a new ProtectionDomain with the given CodeSource and 146 * Permissions. If the permissions object is not null, then 147 * {@code setReadOnly())} will be called on the passed in 148 * Permissions object. The only permissions granted to this domain 149 * are the ones specified; the current Policy will not be consulted. 150 * 151 * @param codesource the codesource associated with this domain 152 * @param permissions the permissions granted to this domain 153 */ 154 public ProtectionDomain(CodeSource codesource, 155 PermissionCollection permissions) { 156 this.codesource = codesource; 157 if (permissions != null) { 158 this.permissions = permissions; 159 this.permissions.setReadOnly(); 160 if (permissions instanceof Permissions && 161 ((Permissions)permissions).allPermission != null) { 162 hasAllPerm = true; 163 } 164 } 165 this.classloader = null; 166 this.principals = new Principal[0]; 167 staticPermissions = true; 168 } 169 170 /** 171 * Creates a new ProtectionDomain qualified by the given CodeSource, 172 * Permissions, ClassLoader and array of Principals. If the 173 * permissions object is not null, then {@code setReadOnly()} 174 * will be called on the passed in Permissions object. 175 * The permissions granted to this domain are dynamic; they include 176 * both the static permissions passed to this constructor, and any 177 * permissions granted to this domain by the current Policy at the 178 * time a permission is checked. 179 * <p> 180 * This constructor is typically used by 181 * {@link SecureClassLoader ClassLoaders} 182 * and {@link DomainCombiner DomainCombiners} which delegate to 183 * {@code Policy} to actively associate the permissions granted to 184 * this domain. This constructor affords the 185 * Policy provider the opportunity to augment the supplied 186 * PermissionCollection to reflect policy changes. 187 * 188 * @param codesource the CodeSource associated with this domain 189 * @param permissions the permissions granted to this domain 190 * @param classloader the ClassLoader associated with this domain 191 * @param principals the array of Principals associated with this 192 * domain. The contents of the array are copied to protect against 193 * subsequent modification. 194 * @see Policy#refresh 195 * @see Policy#getPermissions(ProtectionDomain) 196 * @since 1.4 197 */ 198 public ProtectionDomain(CodeSource codesource, 199 PermissionCollection permissions, 200 ClassLoader classloader, 201 Principal[] principals) { 202 this.codesource = codesource; 203 if (permissions != null) { 204 this.permissions = permissions; 205 this.permissions.setReadOnly(); 206 if (permissions instanceof Permissions && 207 ((Permissions)permissions).allPermission != null) { 208 hasAllPerm = true; 209 } 210 } 211 this.classloader = classloader; 212 this.principals = (principals != null ? principals.clone(): 213 new Principal[0]); 214 staticPermissions = false; 215 } 216 217 /** 218 * Returns the CodeSource of this domain. 219 * @return the CodeSource of this domain which may be null. 220 * @since 1.2 221 */ 222 public final CodeSource getCodeSource() { 223 return this.codesource; 224 } 225 226 227 /** 228 * Returns the ClassLoader of this domain. 229 * @return the ClassLoader of this domain which may be null. 230 * 231 * @since 1.4 232 */ 233 public final ClassLoader getClassLoader() { 234 return this.classloader; 235 } 236 237 238 /** 239 * Returns an array of principals for this domain. 240 * @return a non-null array of principals for this domain. 241 * Returns a new array each time this method is called. 242 * 243 * @since 1.4 244 */ 245 public final Principal[] getPrincipals() { 246 return this.principals.clone(); 247 } 248 249 /** 250 * Returns the static permissions granted to this domain. 251 * 252 * @return the static set of permissions for this domain which may be null. 253 * @see Policy#refresh 254 * @see Policy#getPermissions(ProtectionDomain) 255 */ 256 public final PermissionCollection getPermissions() { 257 return permissions; 258 } 259 260 /** 261 * Check and see if this ProtectionDomain implies the permissions 262 * expressed in the Permission object. 263 * <p> 264 * The set of permissions evaluated is a function of whether the 265 * ProtectionDomain was constructed with a static set of permissions 266 * or it was bound to a dynamically mapped set of permissions. 267 * <p> 268 * If the ProtectionDomain was constructed to a 269 * {@link #ProtectionDomain(CodeSource, PermissionCollection) 270 * statically bound} PermissionCollection then the permission will 271 * only be checked against the PermissionCollection supplied at 272 * construction. 273 * <p> 274 * However, if the ProtectionDomain was constructed with 275 * the constructor variant which supports 276 * {@link #ProtectionDomain(CodeSource, PermissionCollection, 277 * ClassLoader, java.security.Principal[]) dynamically binding} 278 * permissions, then the permission will be checked against the 279 * combination of the PermissionCollection supplied at construction and 280 * the current Policy binding. 281 * 282 * @param permission the Permission object to check. 283 * 284 * @return true if "permission" is implicit to this ProtectionDomain. 285 */ 286 public boolean implies(Permission permission) { 287 288 if (hasAllPerm) { 289 // internal permission collection already has AllPermission - 290 // no need to go to policy 291 return true; 292 } 293 294 if (!staticPermissions && 295 Policy.getPolicyNoCheck().implies(this, permission)) 296 return true; 297 if (permissions != null) 298 return permissions.implies(permission); 299 300 return false; 301 } 302 303 // called by the VM -- do not remove 304 boolean impliesCreateAccessControlContext() { 305 return implies(SecurityConstants.CREATE_ACC_PERMISSION); 306 } 307 308 /** 309 * Convert a ProtectionDomain to a String. 310 */ 311 @Override public String toString() { 312 String pals = "<no principals>"; 313 if (principals != null && principals.length > 0) { 314 StringBuilder palBuf = new StringBuilder("(principals "); 315 316 for (int i = 0; i < principals.length; i++) { 317 palBuf.append(principals[i].getClass().getName() + 318 " \"" + principals[i].getName() + 319 "\""); 320 if (i < principals.length-1) 321 palBuf.append(",\n"); 322 else 323 palBuf.append(")\n"); 324 } 325 pals = palBuf.toString(); 326 } 327 328 // Check if policy is set; we don't want to load 329 // the policy prematurely here 330 PermissionCollection pc = Policy.isSet() && seeAllp() ? 331 mergePermissions(): 332 getPermissions(); 333 334 return "ProtectionDomain "+ 335 " "+codesource+"\n"+ 336 " "+classloader+"\n"+ 337 " "+pals+"\n"+ 338 " "+pc+"\n"; 339 } 340 341 /** 342 * Return true (merge policy permissions) in the following cases: 343 * 344 * . SecurityManager is null 345 * 346 * . SecurityManager is not null, 347 * debug is not null, 348 * SecurityManager impelmentation is in bootclasspath, 349 * Policy implementation is in bootclasspath 350 * (the bootclasspath restrictions avoid recursion) 351 * 352 * . SecurityManager is not null, 353 * debug is null, 354 * caller has Policy.getPolicy permission 355 */ 356 private static boolean seeAllp() { 357 SecurityManager sm = System.getSecurityManager(); 358 359 if (sm == null) { 360 return true; 361 } else { 362 if (debug != null) { 363 if (sm.getClass().getClassLoader() == null && 364 Policy.getPolicyNoCheck().getClass().getClassLoader() 365 == null) { 366 return true; 367 } 368 } else { 369 try { 370 sm.checkPermission(SecurityConstants.GET_POLICY_PERMISSION); 371 return true; 372 } catch (SecurityException se) { 373 // fall thru and return false 374 } 375 } 376 } 377 378 return false; 379 } 380 381 private PermissionCollection mergePermissions() { 382 if (staticPermissions) 383 return permissions; 384 385 PermissionCollection perms = 386 java.security.AccessController.doPrivileged 387 (new java.security.PrivilegedAction<>() { 388 public PermissionCollection run() { 389 Policy p = Policy.getPolicyNoCheck(); 390 return p.getPermissions(ProtectionDomain.this); 391 } 392 }); 393 394 Permissions mergedPerms = new Permissions(); 395 int swag = 32; 396 int vcap = 8; 397 Enumeration<Permission> e; 398 List<Permission> pdVector = new ArrayList<>(vcap); 399 List<Permission> plVector = new ArrayList<>(swag); 400 401 // 402 // Build a vector of domain permissions for subsequent merge 403 if (permissions != null) { 404 synchronized (permissions) { 405 e = permissions.elements(); 406 while (e.hasMoreElements()) { 407 pdVector.add(e.nextElement()); 408 } 409 } 410 } 411 412 // 413 // Build a vector of Policy permissions for subsequent merge 414 if (perms != null) { 415 synchronized (perms) { 416 e = perms.elements(); 417 while (e.hasMoreElements()) { 418 plVector.add(e.nextElement()); 419 vcap++; 420 } 421 } 422 } 423 424 if (perms != null && permissions != null) { 425 // 426 // Weed out the duplicates from the policy. Unless a refresh 427 // has occurred since the pd was consed this should result in 428 // an empty vector. 429 synchronized (permissions) { 430 e = permissions.elements(); // domain vs policy 431 while (e.hasMoreElements()) { 432 Permission pdp = e.nextElement(); 433 Class<?> pdpClass = pdp.getClass(); 434 String pdpActions = pdp.getActions(); 435 String pdpName = pdp.getName(); 436 for (int i = 0; i < plVector.size(); i++) { 437 Permission pp = plVector.get(i); 438 if (pdpClass.isInstance(pp)) { 439 // The equals() method on some permissions 440 // have some side effects so this manual 441 // comparison is sufficient. 442 if (pdpName.equals(pp.getName()) && 443 pdpActions.equals(pp.getActions())) { 444 plVector.remove(i); 445 break; 446 } 447 } 448 } 449 } 450 } 451 } 452 453 if (perms !=null) { 454 // the order of adding to merged perms and permissions 455 // needs to preserve the bugfix 4301064 456 457 for (int i = plVector.size()-1; i >= 0; i--) { 458 mergedPerms.add(plVector.get(i)); 459 } 460 } 461 if (permissions != null) { 462 for (int i = pdVector.size()-1; i >= 0; i--) { 463 mergedPerms.add(pdVector.get(i)); 464 } 465 } 466 467 return mergedPerms; 468 } 469 470 /** 471 * A cache of ProtectionDomains and their Permissions. 472 * 473 * This class stores ProtectionDomains as weak keys in a ConcurrentHashMap 474 * with additional support for checking and removing weak keys that are no 475 * longer in use. 476 */ 477 private static class PDCache implements ProtectionDomainCache { 478 private final ConcurrentHashMap<WeakProtectionDomainKey, 479 PermissionCollection> 480 pdMap = new ConcurrentHashMap<>(); 481 private final ReferenceQueue<Key> queue = new ReferenceQueue<>(); 482 483 @Override 484 public void put(ProtectionDomain pd, PermissionCollection pc) { 485 processQueue(queue, pdMap); 486 WeakProtectionDomainKey weakPd = 487 new WeakProtectionDomainKey(pd, queue); 488 pdMap.putIfAbsent(weakPd, pc); 489 } 490 491 @Override 492 public PermissionCollection get(ProtectionDomain pd) { 493 processQueue(queue, pdMap); 494 WeakProtectionDomainKey weakPd = 495 new WeakProtectionDomainKey(pd, queue); 496 return pdMap.get(weakPd); 497 } 498 499 /** 500 * Removes weak keys from the map that have been enqueued 501 * on the reference queue and are no longer in use. 502 */ 503 private static void processQueue(ReferenceQueue<Key> queue, 504 ConcurrentHashMap<? extends 505 WeakReference<Key>, ?> pdMap) { 506 Reference<? extends Key> ref; 507 while ((ref = queue.poll()) != null) { 508 pdMap.remove(ref); 509 } 510 } 511 } 512 513 /** 514 * A weak key for a ProtectionDomain. 515 */ 516 private static class WeakProtectionDomainKey extends WeakReference<Key> { 517 /** 518 * Saved value of the referent's identity hash code, to maintain 519 * a consistent hash code after the referent has been cleared 520 */ 521 private final int hash; 522 523 /** 524 * A key representing a null ProtectionDomain. 525 */ 526 private static final Key NULL_KEY = new Key(); 527 528 /** 529 * Create a new WeakProtectionDomain with the specified domain and 530 * registered with a queue. 531 */ 532 WeakProtectionDomainKey(ProtectionDomain pd, ReferenceQueue<Key> rq) { 533 this((pd == null ? NULL_KEY : pd.key), rq); 534 } 535 536 private WeakProtectionDomainKey(Key key, ReferenceQueue<Key> rq) { 537 super(key, rq); 538 hash = key.hashCode(); 539 } 540 541 /** 542 * Returns the identity hash code of the original referent. 543 */ 544 @Override 545 public int hashCode() { 546 return hash; 547 } 548 549 /** 550 * Returns true if the given object is an identical 551 * WeakProtectionDomainKey instance, or, if this object's referent 552 * has not been cleared and the given object is another 553 * WeakProtectionDomainKey instance with an identical non-null 554 * referent as this one. 555 */ 556 @Override 557 public boolean equals(Object obj) { 558 if (obj == this) { 559 return true; 560 } 561 562 if (obj instanceof WeakProtectionDomainKey) { 563 Object referent = get(); 564 return (referent != null) && 565 (referent == ((WeakProtectionDomainKey)obj).get()); 566 } else { 567 return false; 568 } 569 } 570 } 571 }