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