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.WeakReference; 29 import java.util.ArrayList; 30 import java.util.Enumeration; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.WeakHashMap; 34 import sun.misc.JavaSecurityProtectionDomainAccess; 35 import static sun.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache; 36 import sun.security.util.Debug; 37 import sun.security.util.SecurityConstants; 38 import sun.misc.JavaSecurityAccess; 39 import sun.misc.SharedSecrets; 40 41 /** 42 * 43 *<p> 44 * This 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 */ 59 60 public class ProtectionDomain { 61 private static class JavaSecurityAccessImpl implements JavaSecurityAccess { 62 63 private JavaSecurityAccessImpl() { 64 } 65 66 @Override 67 public <T> T doIntersectionPrivilege( 68 PrivilegedAction<T> action, 69 final AccessControlContext stack, 70 final AccessControlContext context) { 71 if (action == null) { 72 throw new NullPointerException(); 73 } 74 75 return AccessController.doPrivileged( 76 action, 77 getCombinedACC(context, stack) 78 ); 79 } 80 81 @Override 82 public <T> T doIntersectionPrivilege( 83 PrivilegedAction<T> action, 84 AccessControlContext context) { 85 return doIntersectionPrivilege(action, 86 AccessController.getContext(), context); 87 } 88 89 private static AccessControlContext getCombinedACC(AccessControlContext context, AccessControlContext stack) { 90 AccessControlContext acc = new AccessControlContext(context, stack.getCombiner(), true); 91 92 return new AccessControlContext(stack.getContext(), acc).optimize(); 93 } 94 } 95 96 static { 97 // Set up JavaSecurityAccess in SharedSecrets 98 SharedSecrets.setJavaSecurityAccess(new JavaSecurityAccessImpl()); 99 } 100 101 /* CodeSource */ 102 private CodeSource codesource ; 103 104 /* ClassLoader the protection domain was consed from */ 105 private ClassLoader classloader; 106 107 /* Principals running-as within this protection domain */ 108 private Principal[] principals; 109 110 /* the rights this protection domain is granted */ 111 private PermissionCollection permissions; 112 113 /* if the permissions object has AllPermission */ 114 private boolean hasAllPerm = false; 115 116 /* the PermissionCollection is static (pre 1.4 constructor) 117 or dynamic (via a policy refresh) */ 118 private boolean staticPermissions; 119 120 /* 121 * An object used as a key when the ProtectionDomain is stored in a Map. 122 */ 123 final Key key = new Key(); 124 125 private static final Debug debug = Debug.getInstance("domain"); 126 127 /** 128 * Creates a new ProtectionDomain with the given CodeSource and 129 * Permissions. If the permissions object is not null, then 130 * {@code setReadOnly())} will be called on the passed in 131 * Permissions object. The only permissions granted to this domain 132 * are the ones specified; the current Policy will not be consulted. 133 * 134 * @param codesource the codesource associated with this domain 135 * @param permissions the permissions granted to this domain 136 */ 137 public ProtectionDomain(CodeSource codesource, 138 PermissionCollection permissions) { 139 this.codesource = codesource; 140 if (permissions != null) { 141 this.permissions = permissions; 142 this.permissions.setReadOnly(); 143 if (permissions instanceof Permissions && 144 ((Permissions)permissions).allPermission != null) { 145 hasAllPerm = true; 146 } 147 } 148 this.classloader = null; 149 this.principals = new Principal[0]; 150 staticPermissions = true; 151 } 152 153 /** 154 * Creates a new ProtectionDomain qualified by the given CodeSource, 155 * Permissions, ClassLoader and array of Principals. If the 156 * permissions object is not null, then {@code setReadOnly()} 157 * will be called on the passed in Permissions object. 158 * The permissions granted to this domain are dynamic; they include 159 * both the static permissions passed to this constructor, and any 160 * permissions granted to this domain by the current Policy at the 161 * time a permission is checked. 162 * <p> 163 * This constructor is typically used by 164 * {@link SecureClassLoader ClassLoaders} 165 * and {@link DomainCombiner DomainCombiners} which delegate to 166 * {@code Policy} to actively associate the permissions granted to 167 * this domain. This constructor affords the 168 * Policy provider the opportunity to augment the supplied 169 * PermissionCollection to reflect policy changes. 170 * 171 * @param codesource the CodeSource associated with this domain 172 * @param permissions the permissions granted to this domain 173 * @param classloader the ClassLoader associated with this domain 174 * @param principals the array of Principals associated with this 175 * domain. The contents of the array are copied to protect against 176 * subsequent modification. 177 * @see Policy#refresh 178 * @see Policy#getPermissions(ProtectionDomain) 179 * @since 1.4 180 */ 181 public ProtectionDomain(CodeSource codesource, 182 PermissionCollection permissions, 183 ClassLoader classloader, 184 Principal[] principals) { 185 this.codesource = codesource; 186 if (permissions != null) { 187 this.permissions = permissions; 188 this.permissions.setReadOnly(); 189 if (permissions instanceof Permissions && 190 ((Permissions)permissions).allPermission != null) { 191 hasAllPerm = true; 192 } 193 } 194 this.classloader = classloader; 195 this.principals = (principals != null ? principals.clone(): 196 new Principal[0]); 197 staticPermissions = false; 198 } 199 200 /** 201 * Returns the CodeSource of this domain. 202 * @return the CodeSource of this domain which may be null. 203 * @since 1.2 204 */ 205 public final CodeSource getCodeSource() { 206 return this.codesource; 207 } 208 209 210 /** 211 * Returns the ClassLoader of this domain. 212 * @return the ClassLoader of this domain which may be null. 213 * 214 * @since 1.4 215 */ 216 public final ClassLoader getClassLoader() { 217 return this.classloader; 218 } 219 220 221 /** 222 * Returns an array of principals for this domain. 223 * @return a non-null array of principals for this domain. 224 * Returns a new array each time this method is called. 225 * 226 * @since 1.4 227 */ 228 public final Principal[] getPrincipals() { 229 return this.principals.clone(); 230 } 231 232 /** 233 * Returns the static permissions granted to this domain. 234 * 235 * @return the static set of permissions for this domain which may be null. 236 * @see Policy#refresh 237 * @see Policy#getPermissions(ProtectionDomain) 238 */ 239 public final PermissionCollection getPermissions() { 240 return permissions; 241 } 242 243 /** 244 * Check and see if this ProtectionDomain implies the permissions 245 * expressed in the Permission object. 246 * <p> 247 * The set of permissions evaluated is a function of whether the 248 * ProtectionDomain was constructed with a static set of permissions 249 * or it was bound to a dynamically mapped set of permissions. 250 * <p> 251 * If the ProtectionDomain was constructed to a 252 * {@link #ProtectionDomain(CodeSource, PermissionCollection) 253 * statically bound} PermissionCollection then the permission will 254 * only be checked against the PermissionCollection supplied at 255 * construction. 256 * <p> 257 * However, if the ProtectionDomain was constructed with 258 * the constructor variant which supports 259 * {@link #ProtectionDomain(CodeSource, PermissionCollection, 260 * ClassLoader, java.security.Principal[]) dynamically binding} 261 * permissions, then the permission will be checked against the 262 * combination of the PermissionCollection supplied at construction and 263 * the current Policy binding. 264 * 265 * @param permission the Permission object to check. 266 * 267 * @return true if "permission" is implicit to this ProtectionDomain. 268 */ 269 public boolean implies(Permission permission) { 270 271 if (hasAllPerm) { 272 // internal permission collection already has AllPermission - 273 // no need to go to policy 274 return true; 275 } 276 277 if (!staticPermissions && 278 Policy.getPolicyNoCheck().implies(this, permission)) 279 return true; 280 if (permissions != null) 281 return permissions.implies(permission); 282 283 return false; 284 } 285 286 // called by the VM -- do not remove 287 boolean impliesCreateAccessControlContext() { 288 return implies(SecurityConstants.CREATE_ACC_PERMISSION); 289 } 290 291 /** 292 * Convert a ProtectionDomain to a String. 293 */ 294 @Override public String toString() { 295 String pals = "<no principals>"; 296 if (principals != null && principals.length > 0) { 297 StringBuilder palBuf = new StringBuilder("(principals "); 298 299 for (int i = 0; i < principals.length; i++) { 300 palBuf.append(principals[i].getClass().getName() + 301 " \"" + principals[i].getName() + 302 "\""); 303 if (i < principals.length-1) 304 palBuf.append(",\n"); 305 else 306 palBuf.append(")\n"); 307 } 308 pals = palBuf.toString(); 309 } 310 311 // Check if policy is set; we don't want to load 312 // the policy prematurely here 313 PermissionCollection pc = Policy.isSet() && seeAllp() ? 314 mergePermissions(): 315 getPermissions(); 316 317 return "ProtectionDomain "+ 318 " "+codesource+"\n"+ 319 " "+classloader+"\n"+ 320 " "+pals+"\n"+ 321 " "+pc+"\n"; 322 } 323 324 /** 325 * Return true (merge policy permissions) in the following cases: 326 * 327 * . SecurityManager is null 328 * 329 * . SecurityManager is not null, 330 * debug is not null, 331 * SecurityManager impelmentation is in bootclasspath, 332 * Policy implementation is in bootclasspath 333 * (the bootclasspath restrictions avoid recursion) 334 * 335 * . SecurityManager is not null, 336 * debug is null, 337 * caller has Policy.getPolicy permission 338 */ 339 private static boolean seeAllp() { 340 SecurityManager sm = System.getSecurityManager(); 341 342 if (sm == null) { 343 return true; 344 } else { 345 if (debug != null) { 346 if (sm.getClass().getClassLoader() == null && 347 Policy.getPolicyNoCheck().getClass().getClassLoader() 348 == null) { 349 return true; 350 } 351 } else { 352 try { 353 sm.checkPermission(SecurityConstants.GET_POLICY_PERMISSION); 354 return true; 355 } catch (SecurityException se) { 356 // fall thru and return false 357 } 358 } 359 } 360 361 return false; 362 } 363 364 private PermissionCollection mergePermissions() { 365 if (staticPermissions) 366 return permissions; 367 368 PermissionCollection perms = 369 java.security.AccessController.doPrivileged 370 (new java.security.PrivilegedAction<PermissionCollection>() { 371 public PermissionCollection run() { 372 Policy p = Policy.getPolicyNoCheck(); 373 return p.getPermissions(ProtectionDomain.this); 374 } 375 }); 376 377 Permissions mergedPerms = new Permissions(); 378 int swag = 32; 379 int vcap = 8; 380 Enumeration<Permission> e; 381 List<Permission> pdVector = new ArrayList<>(vcap); 382 List<Permission> plVector = new ArrayList<>(swag); 383 384 // 385 // Build a vector of domain permissions for subsequent merge 386 if (permissions != null) { 387 synchronized (permissions) { 388 e = permissions.elements(); 389 while (e.hasMoreElements()) { 390 pdVector.add(e.nextElement()); 391 } 392 } 393 } 394 395 // 396 // Build a vector of Policy permissions for subsequent merge 397 if (perms != null) { 398 synchronized (perms) { 399 e = perms.elements(); 400 while (e.hasMoreElements()) { 401 plVector.add(e.nextElement()); 402 vcap++; 403 } 404 } 405 } 406 407 if (perms != null && permissions != null) { 408 // 409 // Weed out the duplicates from the policy. Unless a refresh 410 // has occurred since the pd was consed this should result in 411 // an empty vector. 412 synchronized (permissions) { 413 e = permissions.elements(); // domain vs policy 414 while (e.hasMoreElements()) { 415 Permission pdp = e.nextElement(); 416 Class<?> pdpClass = pdp.getClass(); 417 String pdpActions = pdp.getActions(); 418 String pdpName = pdp.getName(); 419 for (int i = 0; i < plVector.size(); i++) { 420 Permission pp = plVector.get(i); 421 if (pdpClass.isInstance(pp)) { 422 // The equals() method on some permissions 423 // have some side effects so this manual 424 // comparison is sufficient. 425 if (pdpName.equals(pp.getName()) && 426 pdpActions.equals(pp.getActions())) { 427 plVector.remove(i); 428 break; 429 } 430 } 431 } 432 } 433 } 434 } 435 436 if (perms !=null) { 437 // the order of adding to merged perms and permissions 438 // needs to preserve the bugfix 4301064 439 440 for (int i = plVector.size()-1; i >= 0; i--) { 441 mergedPerms.add(plVector.get(i)); 442 } 443 } 444 if (permissions != null) { 445 for (int i = pdVector.size()-1; i >= 0; i--) { 446 mergedPerms.add(pdVector.get(i)); 447 } 448 } 449 450 return mergedPerms; 451 } 452 453 /** 454 * Used for storing ProtectionDomains as keys in a Map. 455 */ 456 final static class Key {} 457 458 // A cache of ProtectionDomains and their Permissions 459 private static class PDCache implements ProtectionDomainCache { 460 // We must wrap the PermissionCollection in a WeakReference as there 461 // are some PermissionCollections which contain strong references 462 // back to a ProtectionDomain and otherwise would never be removed 463 // from the WeakHashMap 464 private final Map<Key, WeakReference<PermissionCollection>> 465 map = new WeakHashMap<>(); 466 467 @Override 468 public synchronized void put(ProtectionDomain pd, 469 PermissionCollection pc) { 470 map.put(pd == null ? null : pd.key, new WeakReference<>(pc)); 471 } 472 473 @Override 474 public synchronized PermissionCollection get(ProtectionDomain pd) { 475 WeakReference<PermissionCollection> ref = 476 map.get(pd == null ? null : pd.key); 477 return ref == null ? null : ref.get(); 478 } 479 } 480 481 static { 482 SharedSecrets.setJavaSecurityProtectionDomainAccess( 483 new JavaSecurityProtectionDomainAccess() { 484 @Override 485 public ProtectionDomainCache getProtectionDomainCache() { 486 return new PDCache(); 487 } 488 }); 489 } 490 }