1 /* 2 * Copyright (c) 1997, 2016, 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.SoftReference; 31 import java.lang.ref.WeakReference; 32 import java.util.ArrayList; 33 import java.util.Enumeration; 34 import java.util.List; 35 import java.util.concurrent.ConcurrentHashMap; 36 import jdk.internal.misc.JavaSecurityAccess; 37 import jdk.internal.misc.JavaSecurityProtectionDomainAccess; 38 import static jdk.internal.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache; 39 import jdk.internal.misc.SharedSecrets; 40 import sun.security.provider.PolicyFile; 41 import sun.security.util.Debug; 42 import sun.security.util.FilePermCompat; 43 import sun.security.util.SecurityConstants; 44 45 /** 46 * The ProtectionDomain class encapsulates the characteristics of a domain, 47 * which encloses a set of classes whose instances are granted a set 48 * of permissions when being executed on behalf of a given set of Principals. 49 * <p> 50 * A static set of permissions can be bound to a ProtectionDomain when it is 51 * constructed; such permissions are granted to the domain regardless of the 52 * Policy in force. However, to support dynamic security policies, a 53 * ProtectionDomain can also be constructed such that it is dynamically 54 * mapped to a set of permissions by the current Policy whenever a permission 55 * is checked. 56 * 57 * @author Li Gong 58 * @author Roland Schemers 59 * @author Gary Ellison 60 */ 61 62 public class ProtectionDomain { 63 64 private static class JavaSecurityAccessImpl implements JavaSecurityAccess { 65 66 private JavaSecurityAccessImpl() { 67 } 68 69 @Override 70 public <T> T doIntersectionPrivilege( 71 PrivilegedAction<T> action, 72 final AccessControlContext stack, 73 final AccessControlContext context) { 74 if (action == null) { 75 throw new NullPointerException(); 76 } 77 78 return AccessController.doPrivileged( 79 action, 80 getCombinedACC(context, stack) 81 ); 82 } 83 84 @Override 85 public <T> T doIntersectionPrivilege( 86 PrivilegedAction<T> action, 87 AccessControlContext context) { 88 return doIntersectionPrivilege(action, 89 AccessController.getContext(), context); 90 } 91 92 private static AccessControlContext getCombinedACC( 93 AccessControlContext context, AccessControlContext stack) { 94 AccessControlContext acc = 95 new AccessControlContext(context, stack.getCombiner(), true); 96 97 return new AccessControlContext(stack.getContext(), acc).optimize(); 98 } 99 } 100 101 static { 102 // setup SharedSecrets to allow access to doIntersectionPrivilege 103 // methods and ProtectionDomain cache 104 SharedSecrets.setJavaSecurityAccess(new JavaSecurityAccessImpl()); 105 SharedSecrets.setJavaSecurityProtectionDomainAccess( 106 new JavaSecurityProtectionDomainAccess() { 107 @Override 108 public ProtectionDomainCache getProtectionDomainCache() { 109 return new PDCache(); 110 } 111 }); 112 } 113 114 /** 115 * Used for storing ProtectionDomains as keys in a Map. 116 */ 117 static final class Key {} 118 119 /* CodeSource */ 120 private CodeSource codesource ; 121 122 /* ClassLoader the protection domain was consed from */ 123 private ClassLoader classloader; 124 125 /* Principals running-as within this protection domain */ 126 private Principal[] principals; 127 128 /* the rights this protection domain is granted */ 129 private PermissionCollection permissions; 130 131 /* if the permissions object has AllPermission */ 132 private boolean hasAllPerm = false; 133 134 /* the PermissionCollection is static (pre 1.4 constructor) 135 or dynamic (via a policy refresh) */ 136 private final boolean staticPermissions; 137 138 /* 139 * An object used as a key when the ProtectionDomain is stored in a Map. 140 */ 141 final Key key = new Key(); 142 143 /** 144 * Creates a new ProtectionDomain with the given CodeSource and 145 * Permissions. If the permissions object is not null, then 146 * {@code setReadOnly()} will be called on the passed in 147 * Permissions object. 148 * <p> 149 * The permissions granted to this domain are static, i.e. 150 * invoking the {@link #staticPermissionsOnly()} method returns true. 151 * They contain only the ones passed to this constructor and 152 * the current Policy will not be consulted. 153 * 154 * @param codesource the codesource associated with this domain 155 * @param permissions the permissions granted to this domain 156 */ 157 public ProtectionDomain(CodeSource codesource, 158 PermissionCollection permissions) { 159 this.codesource = codesource; 160 if (permissions != null) { 161 this.permissions = permissions; 162 this.permissions.setReadOnly(); 163 if (permissions instanceof Permissions && 164 ((Permissions)permissions).allPermission != null) { 165 hasAllPerm = true; 166 } 167 } 168 this.classloader = null; 169 this.principals = new Principal[0]; 170 staticPermissions = true; 171 } 172 173 /** 174 * Creates a new ProtectionDomain qualified by the given CodeSource, 175 * Permissions, ClassLoader and array of Principals. If the 176 * permissions object is not null, then {@code setReadOnly()} 177 * will be called on the passed in Permissions object. 178 * <p> 179 * The permissions granted to this domain are dynamic, i.e. 180 * invoking the {@link #staticPermissionsOnly()} method returns false. 181 * They include both the static permissions passed to this constructor, 182 * and any permissions granted to this domain by the current Policy at the 183 * time a permission is checked. 184 * <p> 185 * This constructor is typically used by 186 * {@link SecureClassLoader ClassLoaders} 187 * and {@link DomainCombiner DomainCombiners} which delegate to 188 * {@code Policy} to actively associate the permissions granted to 189 * this domain. This constructor affords the 190 * Policy provider the opportunity to augment the supplied 191 * PermissionCollection to reflect policy changes. 192 * 193 * @param codesource the CodeSource associated with this domain 194 * @param permissions the permissions granted to this domain 195 * @param classloader the ClassLoader associated with this domain 196 * @param principals the array of Principals associated with this 197 * domain. The contents of the array are copied to protect against 198 * subsequent modification. 199 * @see Policy#refresh 200 * @see Policy#getPermissions(ProtectionDomain) 201 * @since 1.4 202 */ 203 public ProtectionDomain(CodeSource codesource, 204 PermissionCollection permissions, 205 ClassLoader classloader, 206 Principal[] principals) { 207 this.codesource = codesource; 208 if (permissions != null) { 209 this.permissions = permissions; 210 this.permissions.setReadOnly(); 211 if (permissions instanceof Permissions && 212 ((Permissions)permissions).allPermission != null) { 213 hasAllPerm = true; 214 } 215 } 216 this.classloader = classloader; 217 this.principals = (principals != null ? principals.clone(): 218 new Principal[0]); 219 staticPermissions = false; 220 } 221 222 /** 223 * Returns the CodeSource of this domain. 224 * @return the CodeSource of this domain which may be null. 225 * @since 1.2 226 */ 227 public final CodeSource getCodeSource() { 228 return this.codesource; 229 } 230 231 232 /** 233 * Returns the ClassLoader of this domain. 234 * @return the ClassLoader of this domain which may be null. 235 * 236 * @since 1.4 237 */ 238 public final ClassLoader getClassLoader() { 239 return this.classloader; 240 } 241 242 243 /** 244 * Returns an array of principals for this domain. 245 * @return a non-null array of principals for this domain. 246 * Returns a new array each time this method is called. 247 * 248 * @since 1.4 249 */ 250 public final Principal[] getPrincipals() { 251 return this.principals.clone(); 252 } 253 254 /** 255 * Returns the static permissions granted to this domain. 256 * 257 * @return the static set of permissions for this domain which may be null. 258 * @see Policy#refresh 259 * @see Policy#getPermissions(ProtectionDomain) 260 */ 261 public final PermissionCollection getPermissions() { 262 return permissions; 263 } 264 265 /** 266 * Returns true if this domain contains only static permissions 267 * and does not check the current {@code Policy} at the time of 268 * permission checking. 269 * 270 * @return true if this domain contains only static permissions. 271 * 272 * @since 9 273 */ 274 public final boolean staticPermissionsOnly() { 275 return this.staticPermissions; 276 } 277 278 /** 279 * Check and see if this ProtectionDomain implies the permissions 280 * expressed in the Permission object. 281 * <p> 282 * The set of permissions evaluated is a function of whether the 283 * ProtectionDomain was constructed with a static set of permissions 284 * or it was bound to a dynamically mapped set of permissions. 285 * <p> 286 * If the {@link #staticPermissionsOnly()} method returns 287 * true, then the permission will only be checked against the 288 * PermissionCollection supplied at construction. 289 * <p> 290 * Otherwise, the permission will be checked against the combination 291 * of the PermissionCollection supplied at construction and 292 * the current Policy binding. 293 * 294 * @param perm the Permission object to check. 295 * 296 * @return true if {@code perm} is implied by this ProtectionDomain. 297 */ 298 public boolean implies(Permission perm) { 299 300 if (hasAllPerm) { 301 // internal permission collection already has AllPermission - 302 // no need to go to policy 303 return true; 304 } 305 306 if (!staticPermissions && 307 Policy.getPolicyNoCheck().implies(this, perm)) { 308 return true; 309 } 310 if (permissions != null) { 311 return permissions.implies(perm); 312 } 313 314 return false; 315 } 316 317 /** 318 * This method has the same logic flow as {@link #implies} except that 319 * when the {@link FilePermCompat#compat} flag is on it ensures 320 * FilePermission compatibility after JDK-8164705. {@code implies()} 321 * is called when compat flag is not on or user has extended 322 * {@code ProtectionDomain}. 323 * 324 * This method is called by {@link AccessControlContext#checkPermission} 325 * and not intended to be called by an application. 326 */ 327 boolean impliesWithAltFilePerm(Permission perm) { 328 329 // If this is a subclass of ProtectionDomain. Call the old method. 330 if (!FilePermCompat.compat || getClass() != ProtectionDomain.class) { 331 return implies(perm); 332 } 333 334 if (hasAllPerm) { 335 // internal permission collection already has AllPermission - 336 // no need to go to policy 337 return true; 338 } 339 340 Permission p2 = null; 341 boolean p2Calculated = false; 342 343 if (!staticPermissions) { 344 Policy policy = Policy.getPolicyNoCheck(); 345 if (policy instanceof PolicyFile) { 346 // The PolicyFile implementation supports compatibility 347 // inside and it also covers the static permissions. 348 return policy.implies(this, perm); 349 } else { 350 if (policy.implies(this, perm)) { 351 return true; 352 } 353 p2 = FilePermCompat.newPermUsingAltPath(perm); 354 p2Calculated = true; 355 if (p2 != null && policy.implies(this, p2)) { 356 return true; 357 } 358 } 359 } 360 if (permissions != null) { 361 if (permissions.implies(perm)) { 362 return true; 363 } else { 364 if (!p2Calculated) { 365 p2 = FilePermCompat.newPermUsingAltPath(perm); 366 } 367 if (p2 != null) { 368 return permissions.implies(p2); 369 } 370 } 371 } 372 return false; 373 } 374 375 // called by the VM -- do not remove 376 boolean impliesCreateAccessControlContext() { 377 return implies(SecurityConstants.CREATE_ACC_PERMISSION); 378 } 379 380 /** 381 * Convert a ProtectionDomain to a String. 382 */ 383 @Override public String toString() { 384 String pals = "<no principals>"; 385 if (principals != null && principals.length > 0) { 386 StringBuilder palBuf = new StringBuilder("(principals "); 387 388 for (int i = 0; i < principals.length; i++) { 389 palBuf.append(principals[i].getClass().getName() + 390 " \"" + principals[i].getName() + 391 "\""); 392 if (i < principals.length-1) 393 palBuf.append(",\n"); 394 else 395 palBuf.append(")\n"); 396 } 397 pals = palBuf.toString(); 398 } 399 400 // Check if policy is set; we don't want to load 401 // the policy prematurely here 402 PermissionCollection pc = Policy.isSet() && seeAllp() ? 403 mergePermissions(): 404 getPermissions(); 405 406 return "ProtectionDomain "+ 407 " "+codesource+"\n"+ 408 " "+classloader+"\n"+ 409 " "+pals+"\n"+ 410 " "+pc+"\n"; 411 } 412 413 /* 414 * holder class for the static field "debug" to delay its initialization 415 */ 416 private static class DebugHolder { 417 private static final Debug debug = Debug.getInstance("domain"); 418 } 419 420 /** 421 * Return true (merge policy permissions) in the following cases: 422 * 423 * . SecurityManager is null 424 * 425 * . SecurityManager is not null, 426 * debug is not null, 427 * SecurityManager impelmentation is in bootclasspath, 428 * Policy implementation is in bootclasspath 429 * (the bootclasspath restrictions avoid recursion) 430 * 431 * . SecurityManager is not null, 432 * debug is null, 433 * caller has Policy.getPolicy permission 434 */ 435 private static boolean seeAllp() { 436 SecurityManager sm = System.getSecurityManager(); 437 438 if (sm == null) { 439 return true; 440 } else { 441 if (DebugHolder.debug != null) { 442 if (sm.getClass().getClassLoader() == null && 443 Policy.getPolicyNoCheck().getClass().getClassLoader() 444 == null) { 445 return true; 446 } 447 } else { 448 try { 449 sm.checkPermission(SecurityConstants.GET_POLICY_PERMISSION); 450 return true; 451 } catch (SecurityException se) { 452 // fall thru and return false 453 } 454 } 455 } 456 457 return false; 458 } 459 460 private PermissionCollection mergePermissions() { 461 if (staticPermissions) 462 return permissions; 463 464 PermissionCollection perms = 465 java.security.AccessController.doPrivileged 466 (new java.security.PrivilegedAction<>() { 467 public PermissionCollection run() { 468 Policy p = Policy.getPolicyNoCheck(); 469 return p.getPermissions(ProtectionDomain.this); 470 } 471 }); 472 473 Permissions mergedPerms = new Permissions(); 474 int swag = 32; 475 int vcap = 8; 476 Enumeration<Permission> e; 477 List<Permission> pdVector = new ArrayList<>(vcap); 478 List<Permission> plVector = new ArrayList<>(swag); 479 480 // 481 // Build a vector of domain permissions for subsequent merge 482 if (permissions != null) { 483 synchronized (permissions) { 484 e = permissions.elements(); 485 while (e.hasMoreElements()) { 486 pdVector.add(e.nextElement()); 487 } 488 } 489 } 490 491 // 492 // Build a vector of Policy permissions for subsequent merge 493 if (perms != null) { 494 synchronized (perms) { 495 e = perms.elements(); 496 while (e.hasMoreElements()) { 497 plVector.add(e.nextElement()); 498 vcap++; 499 } 500 } 501 } 502 503 if (perms != null && permissions != null) { 504 // 505 // Weed out the duplicates from the policy. Unless a refresh 506 // has occurred since the pd was consed this should result in 507 // an empty vector. 508 synchronized (permissions) { 509 e = permissions.elements(); // domain vs policy 510 while (e.hasMoreElements()) { 511 Permission pdp = e.nextElement(); 512 Class<?> pdpClass = pdp.getClass(); 513 String pdpActions = pdp.getActions(); 514 String pdpName = pdp.getName(); 515 for (int i = 0; i < plVector.size(); i++) { 516 Permission pp = plVector.get(i); 517 if (pdpClass.isInstance(pp)) { 518 // The equals() method on some permissions 519 // have some side effects so this manual 520 // comparison is sufficient. 521 if (pdpName.equals(pp.getName()) && 522 pdpActions.equals(pp.getActions())) { 523 plVector.remove(i); 524 break; 525 } 526 } 527 } 528 } 529 } 530 } 531 532 if (perms !=null) { 533 // the order of adding to merged perms and permissions 534 // needs to preserve the bugfix 4301064 535 536 for (int i = plVector.size()-1; i >= 0; i--) { 537 mergedPerms.add(plVector.get(i)); 538 } 539 } 540 if (permissions != null) { 541 for (int i = pdVector.size()-1; i >= 0; i--) { 542 mergedPerms.add(pdVector.get(i)); 543 } 544 } 545 546 return mergedPerms; 547 } 548 549 /** 550 * A cache of ProtectionDomains and their Permissions. 551 * 552 * This class stores ProtectionDomains as weak keys in a ConcurrentHashMap 553 * with additional support for checking and removing weak keys that are no 554 * longer in use. There can be cases where the permission collection may 555 * have a chain of strong references back to the ProtectionDomain, which 556 * ordinarily would prevent the entry from being removed from the map. To 557 * address that, we wrap the permission collection in a SoftReference so 558 * that it can be reclaimed by the garbage collector due to memory demand. 559 */ 560 private static class PDCache implements ProtectionDomainCache { 561 private final ConcurrentHashMap<WeakProtectionDomainKey, 562 SoftReference<PermissionCollection>> 563 pdMap = new ConcurrentHashMap<>(); 564 private final ReferenceQueue<Key> queue = new ReferenceQueue<>(); 565 566 @Override 567 public void put(ProtectionDomain pd, PermissionCollection pc) { 568 processQueue(queue, pdMap); 569 WeakProtectionDomainKey weakPd = 570 new WeakProtectionDomainKey(pd, queue); 571 pdMap.put(weakPd, new SoftReference<>(pc)); 572 } 573 574 @Override 575 public PermissionCollection get(ProtectionDomain pd) { 576 processQueue(queue, pdMap); 577 WeakProtectionDomainKey weakPd = new WeakProtectionDomainKey(pd); 578 SoftReference<PermissionCollection> sr = pdMap.get(weakPd); 579 return (sr == null) ? null : sr.get(); 580 } 581 582 /** 583 * Removes weak keys from the map that have been enqueued 584 * on the reference queue and are no longer in use. 585 */ 586 private static void processQueue(ReferenceQueue<Key> queue, 587 ConcurrentHashMap<? extends 588 WeakReference<Key>, ?> pdMap) { 589 Reference<? extends Key> ref; 590 while ((ref = queue.poll()) != null) { 591 pdMap.remove(ref); 592 } 593 } 594 } 595 596 /** 597 * A weak key for a ProtectionDomain. 598 */ 599 private static class WeakProtectionDomainKey extends WeakReference<Key> { 600 /** 601 * Saved value of the referent's identity hash code, to maintain 602 * a consistent hash code after the referent has been cleared 603 */ 604 private final int hash; 605 606 /** 607 * A key representing a null ProtectionDomain. 608 */ 609 private static final Key NULL_KEY = new Key(); 610 611 /** 612 * Create a new WeakProtectionDomain with the specified domain and 613 * registered with a queue. 614 */ 615 WeakProtectionDomainKey(ProtectionDomain pd, ReferenceQueue<Key> rq) { 616 this((pd == null ? NULL_KEY : pd.key), rq); 617 } 618 619 WeakProtectionDomainKey(ProtectionDomain pd) { 620 this(pd == null ? NULL_KEY : pd.key); 621 } 622 623 private WeakProtectionDomainKey(Key key, ReferenceQueue<Key> rq) { 624 super(key, rq); 625 hash = key.hashCode(); 626 } 627 628 private WeakProtectionDomainKey(Key key) { 629 super(key); 630 hash = key.hashCode(); 631 } 632 633 /** 634 * Returns the identity hash code of the original referent. 635 */ 636 @Override 637 public int hashCode() { 638 return hash; 639 } 640 641 /** 642 * Returns true if the given object is an identical 643 * WeakProtectionDomainKey instance, or, if this object's referent 644 * has not been cleared and the given object is another 645 * WeakProtectionDomainKey instance with an identical non-null 646 * referent as this one. 647 */ 648 @Override 649 public boolean equals(Object obj) { 650 if (obj == this) { 651 return true; 652 } 653 654 if (obj instanceof WeakProtectionDomainKey) { 655 Object referent = get(); 656 return (referent != null) && 657 (referent == ((WeakProtectionDomainKey)obj).get()); 658 } else { 659 return false; 660 } 661 } 662 } 663 }