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