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.Enumeration; 29 import java.util.Hashtable; 30 import java.util.NoSuchElementException; 31 import java.util.Map; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Iterator; 35 import java.util.Collections; 36 import java.io.Serializable; 37 import java.io.ObjectStreamField; 38 import java.io.ObjectOutputStream; 39 import java.io.ObjectInputStream; 40 import java.io.IOException; 41 42 43 /** 44 * This class represents a heterogeneous collection of Permissions. That is, 45 * it contains different types of Permission objects, organized into 46 * PermissionCollections. For example, if any 47 * {@code java.io.FilePermission} objects are added to an instance of 48 * this class, they are all stored in a single 49 * PermissionCollection. It is the PermissionCollection returned by a call to 50 * the {@code newPermissionCollection} method in the FilePermission class. 51 * Similarly, any {@code java.lang.RuntimePermission} objects are 52 * stored in the PermissionCollection returned by a call to the 53 * {@code newPermissionCollection} method in the 54 * RuntimePermission class. Thus, this class represents a collection of 55 * PermissionCollections. 56 * 57 * <p>When the {@code add} method is called to add a Permission, the 58 * Permission is stored in the appropriate PermissionCollection. If no such 59 * collection exists yet, the Permission object's class is determined and the 60 * {@code newPermissionCollection} method is called on that class to create 61 * the PermissionCollection and add it to the Permissions object. If 62 * {@code newPermissionCollection} returns null, then a default 63 * PermissionCollection that uses a hashtable will be created and used. Each 64 * hashtable entry stores a Permission object as both the key and the value. 65 * 66 * <p> Enumerations returned via the {@code elements} method are 67 * not <em>fail-fast</em>. Modifications to a collection should not be 68 * performed while enumerating over that collection. 69 * 70 * @see Permission 71 * @see PermissionCollection 72 * @see AllPermission 73 * 74 * 75 * @author Marianne Mueller 76 * @author Roland Schemers 77 * 78 * @serial exclude 79 */ 80 81 public final class Permissions extends PermissionCollection 82 implements Serializable 83 { 84 /** 85 * Key is permissions Class, value is PermissionCollection for that class. 86 * Not serialized; see serialization section at end of class. 87 */ 88 private transient Map<Class<?>, PermissionCollection> permsMap; 89 90 // optimization. keep track of whether unresolved permissions need to be 91 // checked 92 private transient boolean hasUnresolved = false; 93 94 // optimization. keep track of the AllPermission collection 95 // - package private for ProtectionDomain optimization 96 PermissionCollection allPermission; 97 98 /** 99 * Creates a new Permissions object containing no PermissionCollections. 100 */ 101 public Permissions() { 102 permsMap = new HashMap<Class<?>, PermissionCollection>(11); 103 allPermission = null; 104 } 105 106 /** 107 * Adds a permission object to the PermissionCollection for the class the 108 * permission belongs to. For example, if <i>permission</i> is a 109 * FilePermission, it is added to the FilePermissionCollection stored 110 * in this Permissions object. 111 * 112 * This method creates 113 * a new PermissionCollection object (and adds the permission to it) 114 * if an appropriate collection does not yet exist. <p> 115 * 116 * @param permission the Permission object to add. 117 * 118 * @exception SecurityException if this Permissions object is 119 * marked as readonly. 120 * 121 * @see PermissionCollection#isReadOnly() 122 */ 123 124 public void add(Permission permission) { 125 if (isReadOnly()) 126 throw new SecurityException( 127 "attempt to add a Permission to a readonly Permissions object"); 128 129 PermissionCollection pc; 130 131 synchronized (this) { 132 pc = getPermissionCollection(permission, true); 133 pc.add(permission); 134 } 135 136 // No sync; staleness -> optimizations delayed, which is OK 137 if (permission instanceof AllPermission) { 138 allPermission = pc; 139 } 140 if (permission instanceof UnresolvedPermission) { 141 hasUnresolved = true; 142 } 143 } 144 145 /** 146 * Checks to see if this object's PermissionCollection for permissions of 147 * the specified permission's class implies the permissions 148 * expressed in the <i>permission</i> object. Returns true if the 149 * combination of permissions in the appropriate PermissionCollection 150 * (e.g., a FilePermissionCollection for a FilePermission) together 151 * imply the specified permission. 152 * 153 * <p>For example, suppose there is a FilePermissionCollection in this 154 * Permissions object, and it contains one FilePermission that specifies 155 * "read" access for all files in all subdirectories of the "/tmp" 156 * directory, and another FilePermission that specifies "write" access 157 * for all files in the "/tmp/scratch/foo" directory. 158 * Then if the {@code implies} method 159 * is called with a permission specifying both "read" and "write" access 160 * to files in the "/tmp/scratch/foo" directory, {@code true} is 161 * returned. 162 * 163 * <p>Additionally, if this PermissionCollection contains the 164 * AllPermission, this method will always return true. 165 * <p> 166 * @param permission the Permission object to check. 167 * 168 * @return true if "permission" is implied by the permissions in the 169 * PermissionCollection it 170 * belongs to, false if not. 171 */ 172 173 public boolean implies(Permission permission) { 174 // No sync; staleness -> skip optimization, which is OK 175 if (allPermission != null) { 176 return true; // AllPermission has already been added 177 } else { 178 synchronized (this) { 179 PermissionCollection pc = getPermissionCollection(permission, 180 false); 181 if (pc != null) { 182 return pc.implies(permission); 183 } else { 184 // none found 185 return false; 186 } 187 } 188 } 189 } 190 191 /** 192 * Returns an enumeration of all the Permission objects in all the 193 * PermissionCollections in this Permissions object. 194 * 195 * @return an enumeration of all the Permissions. 196 */ 197 198 public Enumeration<Permission> elements() { 199 // go through each Permissions in the hash table 200 // and call their elements() function. 201 202 synchronized (this) { 203 return new PermissionsEnumerator(permsMap.values().iterator()); 204 } 205 } 206 207 /** 208 * Gets the PermissionCollection in this Permissions object for 209 * permissions whose type is the same as that of <i>p</i>. 210 * For example, if <i>p</i> is a FilePermission, 211 * the FilePermissionCollection 212 * stored in this Permissions object will be returned. 213 * 214 * If createEmpty is true, 215 * this method creates a new PermissionCollection object for the specified 216 * type of permission objects if one does not yet exist. 217 * To do so, it first calls the {@code newPermissionCollection} method 218 * on <i>p</i>. Subclasses of class Permission 219 * override that method if they need to store their permissions in a 220 * particular PermissionCollection object in order to provide the 221 * correct semantics when the {@code PermissionCollection.implies} 222 * method is called. 223 * If the call returns a PermissionCollection, that collection is stored 224 * in this Permissions object. If the call returns null and createEmpty 225 * is true, then 226 * this method instantiates and stores a default PermissionCollection 227 * that uses a hashtable to store its permission objects. 228 * 229 * createEmpty is ignored when creating empty PermissionCollection 230 * for unresolved permissions because of the overhead of determining the 231 * PermissionCollection to use. 232 * 233 * createEmpty should be set to false when this method is invoked from 234 * implies() because it incurs the additional overhead of creating and 235 * adding an empty PermissionCollection that will just return false. 236 * It should be set to true when invoked from add(). 237 */ 238 private PermissionCollection getPermissionCollection(Permission p, 239 boolean createEmpty) { 240 Class<?> c = p.getClass(); 241 242 PermissionCollection pc = permsMap.get(c); 243 244 if (!hasUnresolved && !createEmpty) { 245 return pc; 246 } else if (pc == null) { 247 248 // Check for unresolved permissions 249 pc = (hasUnresolved ? getUnresolvedPermissions(p) : null); 250 251 // if still null, create a new collection 252 if (pc == null && createEmpty) { 253 254 pc = p.newPermissionCollection(); 255 256 // still no PermissionCollection? 257 // We'll give them a PermissionsHash. 258 if (pc == null) 259 pc = new PermissionsHash(); 260 } 261 262 if (pc != null) { 263 permsMap.put(c, pc); 264 } 265 } 266 return pc; 267 } 268 269 /** 270 * Resolves any unresolved permissions of type p. 271 * 272 * @param p the type of unresolved permission to resolve 273 * 274 * @return PermissionCollection containing the unresolved permissions, 275 * or null if there were no unresolved permissions of type p. 276 * 277 */ 278 private PermissionCollection getUnresolvedPermissions(Permission p) 279 { 280 // Called from within synchronized method so permsMap doesn't need lock 281 282 UnresolvedPermissionCollection uc = 283 (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class); 284 285 // we have no unresolved permissions if uc is null 286 if (uc == null) 287 return null; 288 289 List<UnresolvedPermission> unresolvedPerms = 290 uc.getUnresolvedPermissions(p); 291 292 // we have no unresolved permissions of this type if unresolvedPerms is null 293 if (unresolvedPerms == null) 294 return null; 295 296 java.security.cert.Certificate certs[] = null; 297 298 Object signers[] = p.getClass().getSigners(); 299 300 int n = 0; 301 if (signers != null) { 302 for (int j=0; j < signers.length; j++) { 303 if (signers[j] instanceof java.security.cert.Certificate) { 304 n++; 305 } 306 } 307 certs = new java.security.cert.Certificate[n]; 308 n = 0; 309 for (int j=0; j < signers.length; j++) { 310 if (signers[j] instanceof java.security.cert.Certificate) { 311 certs[n++] = (java.security.cert.Certificate)signers[j]; 312 } 313 } 314 } 315 316 PermissionCollection pc = null; 317 synchronized (unresolvedPerms) { 318 int len = unresolvedPerms.size(); 319 for (int i = 0; i < len; i++) { 320 UnresolvedPermission up = unresolvedPerms.get(i); 321 Permission perm = up.resolve(p, certs); 322 if (perm != null) { 323 if (pc == null) { 324 pc = p.newPermissionCollection(); 325 if (pc == null) 326 pc = new PermissionsHash(); 327 } 328 pc.add(perm); 329 } 330 } 331 } 332 return pc; 333 } 334 335 private static final long serialVersionUID = 4858622370623524688L; 336 337 // Need to maintain serialization interoperability with earlier releases, 338 // which had the serializable field: 339 // private Hashtable perms; 340 341 /** 342 * @serialField perms java.util.Hashtable 343 * A table of the Permission classes and PermissionCollections. 344 * @serialField allPermission java.security.PermissionCollection 345 */ 346 private static final ObjectStreamField[] serialPersistentFields = { 347 new ObjectStreamField("perms", Hashtable.class), 348 new ObjectStreamField("allPermission", PermissionCollection.class), 349 }; 350 351 /** 352 * @serialData Default fields. 353 */ 354 /* 355 * Writes the contents of the permsMap field out as a Hashtable for 356 * serialization compatibility with earlier releases. allPermission 357 * unchanged. 358 */ 359 private void writeObject(ObjectOutputStream out) throws IOException { 360 // Don't call out.defaultWriteObject() 361 362 // Copy perms into a Hashtable 363 Hashtable<Class<?>, PermissionCollection> perms = 364 new Hashtable<>(permsMap.size()*2); // no sync; estimate 365 synchronized (this) { 366 perms.putAll(permsMap); 367 } 368 369 // Write out serializable fields 370 ObjectOutputStream.PutField pfields = out.putFields(); 371 372 pfields.put("allPermission", allPermission); // no sync; staleness OK 373 pfields.put("perms", perms); 374 out.writeFields(); 375 } 376 377 /* 378 * Reads in a Hashtable of Class/PermissionCollections and saves them in the 379 * permsMap field. Reads in allPermission. 380 */ 381 private void readObject(ObjectInputStream in) throws IOException, 382 ClassNotFoundException { 383 // Don't call defaultReadObject() 384 385 // Read in serialized fields 386 ObjectInputStream.GetField gfields = in.readFields(); 387 388 // Get allPermission 389 allPermission = (PermissionCollection) gfields.get("allPermission", null); 390 391 // Get permissions 392 // writeObject writes a Hashtable<Class<?>, PermissionCollection> for 393 // the perms key, so this cast is safe, unless the data is corrupt. 394 @SuppressWarnings("unchecked") 395 Hashtable<Class<?>, PermissionCollection> perms = 396 (Hashtable<Class<?>, PermissionCollection>)gfields.get("perms", null); 397 permsMap = new HashMap<Class<?>, PermissionCollection>(perms.size()*2); 398 permsMap.putAll(perms); 399 400 // Set hasUnresolved 401 UnresolvedPermissionCollection uc = 402 (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class); 403 hasUnresolved = (uc != null && uc.elements().hasMoreElements()); 404 } 405 } 406 407 final class PermissionsEnumerator implements Enumeration<Permission> { 408 409 // all the perms 410 private Iterator<PermissionCollection> perms; 411 // the current set 412 private Enumeration<Permission> permset; 413 414 PermissionsEnumerator(Iterator<PermissionCollection> e) { 415 perms = e; 416 permset = getNextEnumWithMore(); 417 } 418 419 // No need to synchronize; caller should sync on object as required 420 public boolean hasMoreElements() { 421 // if we enter with permissionimpl null, we know 422 // there are no more left. 423 424 if (permset == null) 425 return false; 426 427 // try to see if there are any left in the current one 428 429 if (permset.hasMoreElements()) 430 return true; 431 432 // get the next one that has something in it... 433 permset = getNextEnumWithMore(); 434 435 // if it is null, we are done! 436 return (permset != null); 437 } 438 439 // No need to synchronize; caller should sync on object as required 440 public Permission nextElement() { 441 442 // hasMoreElements will update permset to the next permset 443 // with something in it... 444 445 if (hasMoreElements()) { 446 return permset.nextElement(); 447 } else { 448 throw new NoSuchElementException("PermissionsEnumerator"); 449 } 450 451 } 452 453 private Enumeration<Permission> getNextEnumWithMore() { 454 while (perms.hasNext()) { 455 PermissionCollection pc = perms.next(); 456 Enumeration<Permission> next =pc.elements(); 457 if (next.hasMoreElements()) 458 return next; 459 } 460 return null; 461 462 } 463 } 464 465 /** 466 * A PermissionsHash stores a homogeneous set of permissions in a hashtable. 467 * 468 * @see Permission 469 * @see Permissions 470 * 471 * 472 * @author Roland Schemers 473 * 474 * @serial include 475 */ 476 477 final class PermissionsHash extends PermissionCollection 478 implements Serializable 479 { 480 /** 481 * Key and value are (same) permissions objects. 482 * Not serialized; see serialization section at end of class. 483 */ 484 private transient Map<Permission, Permission> permsMap; 485 486 /** 487 * Create an empty PermissionsHash object. 488 */ 489 490 PermissionsHash() { 491 permsMap = new HashMap<Permission, Permission>(11); 492 } 493 494 /** 495 * Adds a permission to the PermissionsHash. 496 * 497 * @param permission the Permission object to add. 498 */ 499 500 public void add(Permission permission) { 501 synchronized (this) { 502 permsMap.put(permission, permission); 503 } 504 } 505 506 /** 507 * Check and see if this set of permissions implies the permissions 508 * expressed in "permission". 509 * 510 * @param permission the Permission object to compare 511 * 512 * @return true if "permission" is a proper subset of a permission in 513 * the set, false if not. 514 */ 515 516 public boolean implies(Permission permission) { 517 // attempt a fast lookup and implies. If that fails 518 // then enumerate through all the permissions. 519 synchronized (this) { 520 Permission p = permsMap.get(permission); 521 522 // If permission is found, then p.equals(permission) 523 if (p == null) { 524 for (Permission p_ : permsMap.values()) { 525 if (p_.implies(permission)) 526 return true; 527 } 528 return false; 529 } else { 530 return true; 531 } 532 } 533 } 534 535 /** 536 * Returns an enumeration of all the Permission objects in the container. 537 * 538 * @return an enumeration of all the Permissions. 539 */ 540 541 public Enumeration<Permission> elements() { 542 // Convert Iterator of Map values into an Enumeration 543 synchronized (this) { 544 return Collections.enumeration(permsMap.values()); 545 } 546 } 547 548 private static final long serialVersionUID = -8491988220802933440L; 549 // Need to maintain serialization interoperability with earlier releases, 550 // which had the serializable field: 551 // private Hashtable perms; 552 /** 553 * @serialField perms java.util.Hashtable 554 * A table of the Permissions (both key and value are same). 555 */ 556 private static final ObjectStreamField[] serialPersistentFields = { 557 new ObjectStreamField("perms", Hashtable.class), 558 }; 559 560 /** 561 * @serialData Default fields. 562 */ 563 /* 564 * Writes the contents of the permsMap field out as a Hashtable for 565 * serialization compatibility with earlier releases. 566 */ 567 private void writeObject(ObjectOutputStream out) throws IOException { 568 // Don't call out.defaultWriteObject() 569 570 // Copy perms into a Hashtable 571 Hashtable<Permission, Permission> perms = 572 new Hashtable<>(permsMap.size()*2); 573 synchronized (this) { 574 perms.putAll(permsMap); 575 } 576 577 // Write out serializable fields 578 ObjectOutputStream.PutField pfields = out.putFields(); 579 pfields.put("perms", perms); 580 out.writeFields(); 581 } 582 583 /* 584 * Reads in a Hashtable of Permission/Permission and saves them in the 585 * permsMap field. 586 */ 587 private void readObject(ObjectInputStream in) throws IOException, 588 ClassNotFoundException { 589 // Don't call defaultReadObject() 590 591 // Read in serialized fields 592 ObjectInputStream.GetField gfields = in.readFields(); 593 594 // Get permissions 595 // writeObject writes a Hashtable<Class<?>, PermissionCollection> for 596 // the perms key, so this cast is safe, unless the data is corrupt. 597 @SuppressWarnings("unchecked") 598 Hashtable<Permission, Permission> perms = 599 (Hashtable<Permission, Permission>)gfields.get("perms", null); 600 permsMap = new HashMap<Permission, Permission>(perms.size()*2); 601 permsMap.putAll(perms); 602 } 603 }