1 /* 2 * Copyright (c) 1997, 2012, 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.Map; 30 import java.util.HashMap; 31 import java.util.Hashtable; 32 import java.util.Collections; 33 import java.io.ObjectStreamField; 34 import java.io.ObjectOutputStream; 35 import java.io.ObjectInputStream; 36 import java.io.IOException; 37 38 /** 39 * The BasicPermission class extends the Permission class, and 40 * can be used as the base class for permissions that want to 41 * follow the same naming convention as BasicPermission. 42 * <P> 43 * The name for a BasicPermission is the name of the given permission 44 * (for example, "exit", 45 * "setFactory", "print.queueJob", etc). The naming 46 * convention follows the hierarchical property naming convention. 47 * An asterisk may appear by itself, or if immediately preceded by a "." 48 * may appear at the end of the name, to signify a wildcard match. 49 * For example, "*" and "java.*" signify a wildcard match, while "*java", "a*b", 50 * and "java*" do not. 51 * <P> 52 * The action string (inherited from Permission) is unused. 53 * Thus, BasicPermission is commonly used as the base class for 54 * "named" permissions 55 * (ones that contain a name but no actions list; you either have the 56 * named permission or you don't.) 57 * Subclasses may implement actions on top of BasicPermission, 58 * if desired. 59 * <p> 60 * <P> 61 * @see java.security.Permission 62 * @see java.security.Permissions 63 * @see java.security.PermissionCollection 64 * @see java.lang.SecurityManager 65 * 66 * @author Marianne Mueller 67 * @author Roland Schemers 68 */ 69 70 public abstract class BasicPermission extends Permission 71 implements java.io.Serializable 72 { 73 74 private static final long serialVersionUID = 6279438298436773498L; 75 76 // does this permission have a wildcard at the end? 77 private transient boolean wildcard; 78 79 // the name without the wildcard on the end 80 private transient String path; 81 82 // is this permission the old-style exitVM permission (pre JDK 1.6)? 83 private transient boolean exitVM; 84 85 /** 86 * initialize a BasicPermission object. Common to all constructors. 87 */ 88 private void init(String name) { 89 if (name == null) 90 throw new NullPointerException("name can't be null"); 91 92 int len = name.length(); 93 94 if (len == 0) { 95 throw new IllegalArgumentException("name can't be empty"); 96 } 97 98 char last = name.charAt(len - 1); 99 100 // Is wildcard or ends with ".*"? 101 if (last == '*' && (len == 1 || name.charAt(len - 2) == '.')) { 102 wildcard = true; 103 if (len == 1) { 104 path = ""; 105 } else { 106 path = name.substring(0, len - 1); 107 } 108 } else { 109 if (name.equals("exitVM")) { 110 wildcard = true; 111 path = "exitVM."; 112 exitVM = true; 113 } else { 114 path = name; 115 } 116 } 117 } 118 119 /** 120 * Creates a new BasicPermission with the specified name. 121 * Name is the symbolic name of the permission, such as 122 * "setFactory", 123 * "print.queueJob", or "topLevelWindow", etc. 124 * 125 * @param name the name of the BasicPermission. 126 * 127 * @throws NullPointerException if <code>name</code> is <code>null</code>. 128 * @throws IllegalArgumentException if <code>name</code> is empty. 129 */ 130 public BasicPermission(String name) { 131 super(name); 132 init(name); 133 } 134 135 136 /** 137 * Creates a new BasicPermission object with the specified name. 138 * The name is the symbolic name of the BasicPermission, and the 139 * actions String is currently unused. 140 * 141 * @param name the name of the BasicPermission. 142 * @param actions ignored. 143 * 144 * @throws NullPointerException if <code>name</code> is <code>null</code>. 145 * @throws IllegalArgumentException if <code>name</code> is empty. 146 */ 147 public BasicPermission(String name, String actions) { 148 super(name); 149 init(name); 150 } 151 152 /** 153 * Checks if the specified permission is "implied" by 154 * this object. 155 * <P> 156 * More specifically, this method returns true if:<p> 157 * <ul> 158 * <li> <i>p</i>'s class is the same as this object's class, and<p> 159 * <li> <i>p</i>'s name equals or (in the case of wildcards) 160 * is implied by this object's 161 * name. For example, "a.b.*" implies "a.b.c". 162 * </ul> 163 * 164 * @param p the permission to check against. 165 * 166 * @return true if the passed permission is equal to or 167 * implied by this permission, false otherwise. 168 */ 169 public boolean implies(Permission p) { 170 if ((p == null) || (p.getClass() != getClass())) 171 return false; 172 173 BasicPermission that = (BasicPermission) p; 174 175 if (this.wildcard) { 176 if (that.wildcard) { 177 // one wildcard can imply another 178 return that.path.startsWith(path); 179 } else { 180 // make sure ap.path is longer so a.b.* doesn't imply a.b 181 return (that.path.length() > this.path.length()) && 182 that.path.startsWith(this.path); 183 } 184 } else { 185 if (that.wildcard) { 186 // a non-wildcard can't imply a wildcard 187 return false; 188 } 189 else { 190 return this.path.equals(that.path); 191 } 192 } 193 } 194 195 /** 196 * Checks two BasicPermission objects for equality. 197 * Checks that <i>obj</i>'s class is the same as this object's class 198 * and has the same name as this object. 199 * <P> 200 * @param obj the object we are testing for equality with this object. 201 * @return true if <i>obj</i>'s class is the same as this object's class 202 * and has the same name as this BasicPermission object, false otherwise. 203 */ 204 public boolean equals(Object obj) { 205 if (obj == this) 206 return true; 207 208 if ((obj == null) || (obj.getClass() != getClass())) 209 return false; 210 211 BasicPermission bp = (BasicPermission) obj; 212 213 return getName().equals(bp.getName()); 214 } 215 216 217 /** 218 * Returns the hash code value for this object. 219 * The hash code used is the hash code of the name, that is, 220 * <code>getName().hashCode()</code>, where <code>getName</code> is 221 * from the Permission superclass. 222 * 223 * @return a hash code value for this object. 224 */ 225 public int hashCode() { 226 return this.getName().hashCode(); 227 } 228 229 /** 230 * Returns the canonical string representation of the actions, 231 * which currently is the empty string "", since there are no actions for 232 * a BasicPermission. 233 * 234 * @return the empty string "". 235 */ 236 public String getActions() { 237 return ""; 238 } 239 240 /** 241 * Returns a new PermissionCollection object for storing BasicPermission 242 * objects. 243 * 244 * <p>BasicPermission objects must be stored in a manner that allows them 245 * to be inserted in any order, but that also enables the 246 * PermissionCollection <code>implies</code> method 247 * to be implemented in an efficient (and consistent) manner. 248 * 249 * @return a new PermissionCollection object suitable for 250 * storing BasicPermissions. 251 */ 252 public PermissionCollection newPermissionCollection() { 253 return new BasicPermissionCollection(this.getClass()); 254 } 255 256 /** 257 * readObject is called to restore the state of the BasicPermission from 258 * a stream. 259 */ 260 private void readObject(ObjectInputStream s) 261 throws IOException, ClassNotFoundException 262 { 263 s.defaultReadObject(); 264 // init is called to initialize the rest of the values. 265 init(getName()); 266 } 267 268 /** 269 * Returns the canonical name of this BasicPermission. 270 * All internal invocations of getName should invoke this method, so 271 * that the pre-JDK 1.6 "exitVM" and current "exitVM.*" permission are 272 * equivalent in equals/hashCode methods. 273 * 274 * @return the canonical name of this BasicPermission. 275 */ 276 final String getCanonicalName() { 277 return exitVM ? "exitVM.*" : getName(); 278 } 279 } 280 281 /** 282 * A BasicPermissionCollection stores a collection 283 * of BasicPermission permissions. BasicPermission objects 284 * must be stored in a manner that allows them to be inserted in any 285 * order, but enable the implies function to evaluate the implies 286 * method in an efficient (and consistent) manner. 287 * 288 * A BasicPermissionCollection handles comparing a permission like "a.b.c.d.e" 289 * with a Permission such as "a.b.*", or "*". 290 * 291 * @see java.security.Permission 292 * @see java.security.Permissions 293 * 294 * 295 * @author Roland Schemers 296 * 297 * @serial include 298 */ 299 300 final class BasicPermissionCollection 301 extends PermissionCollection 302 implements java.io.Serializable 303 { 304 305 private static final long serialVersionUID = 739301742472979399L; 306 307 /** 308 * Key is name, value is permission. All permission objects in 309 * collection must be of the same type. 310 * Not serialized; see serialization section at end of class. 311 */ 312 private transient Map<String, Permission> perms; 313 314 /** 315 * This is set to <code>true</code> if this BasicPermissionCollection 316 * contains a BasicPermission with '*' as its permission name. 317 * 318 * @see #serialPersistentFields 319 */ 320 private boolean all_allowed; 321 322 /** 323 * The class to which all BasicPermissions in this 324 * BasicPermissionCollection belongs. 325 * 326 * @see #serialPersistentFields 327 */ 328 private Class<?> permClass; 329 330 /** 331 * Create an empty BasicPermissionCollection object. 332 * 333 */ 334 335 public BasicPermissionCollection(Class<?> clazz) { 336 perms = new HashMap<String, Permission>(11); 337 all_allowed = false; 338 permClass = clazz; 339 } 340 341 /** 342 * Adds a permission to the BasicPermissions. The key for the hash is 343 * permission.path. 344 * 345 * @param permission the Permission object to add. 346 * 347 * @exception IllegalArgumentException - if the permission is not a 348 * BasicPermission, or if 349 * the permission is not of the 350 * same Class as the other 351 * permissions in this collection. 352 * 353 * @exception SecurityException - if this BasicPermissionCollection object 354 * has been marked readonly 355 */ 356 public void add(Permission permission) { 357 if (! (permission instanceof BasicPermission)) 358 throw new IllegalArgumentException("invalid permission: "+ 359 permission); 360 if (isReadOnly()) 361 throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection"); 362 363 BasicPermission bp = (BasicPermission) permission; 364 365 // make sure we only add new BasicPermissions of the same class 366 // Also check null for compatibility with deserialized form from 367 // previous versions. 368 if (permClass == null) { 369 // adding first permission 370 permClass = bp.getClass(); 371 } else { 372 if (bp.getClass() != permClass) 373 throw new IllegalArgumentException("invalid permission: " + 374 permission); 375 } 376 377 synchronized (this) { 378 perms.put(bp.getCanonicalName(), permission); 379 } 380 381 // No sync on all_allowed; staleness OK 382 if (!all_allowed) { 383 if (bp.getCanonicalName().equals("*")) 384 all_allowed = true; 385 } 386 } 387 388 /** 389 * Check and see if this set of permissions implies the permissions 390 * expressed in "permission". 391 * 392 * @param permission the Permission object to compare 393 * 394 * @return true if "permission" is a proper subset of a permission in 395 * the set, false if not. 396 */ 397 public boolean implies(Permission permission) { 398 if (! (permission instanceof BasicPermission)) 399 return false; 400 401 BasicPermission bp = (BasicPermission) permission; 402 403 // random subclasses of BasicPermission do not imply each other 404 if (bp.getClass() != permClass) 405 return false; 406 407 // short circuit if the "*" Permission was added 408 if (all_allowed) 409 return true; 410 411 // strategy: 412 // Check for full match first. Then work our way up the 413 // path looking for matches on a.b..* 414 415 String path = bp.getCanonicalName(); 416 //System.out.println("check "+path); 417 418 Permission x; 419 420 synchronized (this) { 421 x = perms.get(path); 422 } 423 424 if (x != null) { 425 // we have a direct hit! 426 return x.implies(permission); 427 } 428 429 // work our way up the tree... 430 int last, offset; 431 432 offset = path.length()-1; 433 434 while ((last = path.lastIndexOf(".", offset)) != -1) { 435 436 path = path.substring(0, last+1) + "*"; 437 //System.out.println("check "+path); 438 439 synchronized (this) { 440 x = perms.get(path); 441 } 442 443 if (x != null) { 444 return x.implies(permission); 445 } 446 offset = last -1; 447 } 448 449 // we don't have to check for "*" as it was already checked 450 // at the top (all_allowed), so we just return false 451 return false; 452 } 453 454 /** 455 * Returns an enumeration of all the BasicPermission objects in the 456 * container. 457 * 458 * @return an enumeration of all the BasicPermission objects. 459 */ 460 public Enumeration<Permission> elements() { 461 // Convert Iterator of Map values into an Enumeration 462 synchronized (this) { 463 return Collections.enumeration(perms.values()); 464 } 465 } 466 467 // Need to maintain serialization interoperability with earlier releases, 468 // which had the serializable field: 469 // 470 // @serial the Hashtable is indexed by the BasicPermission name 471 // 472 // private Hashtable permissions; 473 /** 474 * @serialField permissions java.util.Hashtable 475 * The BasicPermissions in this BasicPermissionCollection. 476 * All BasicPermissions in the collection must belong to the same class. 477 * The Hashtable is indexed by the BasicPermission name; the value 478 * of the Hashtable entry is the permission. 479 * @serialField all_allowed boolean 480 * This is set to <code>true</code> if this BasicPermissionCollection 481 * contains a BasicPermission with '*' as its permission name. 482 * @serialField permClass java.lang.Class 483 * The class to which all BasicPermissions in this 484 * BasicPermissionCollection belongs. 485 */ 486 private static final ObjectStreamField[] serialPersistentFields = { 487 new ObjectStreamField("permissions", Hashtable.class), 488 new ObjectStreamField("all_allowed", Boolean.TYPE), 489 new ObjectStreamField("permClass", Class.class), 490 }; 491 492 /** 493 * @serialData Default fields. 494 */ 495 /* 496 * Writes the contents of the perms field out as a Hashtable for 497 * serialization compatibility with earlier releases. all_allowed 498 * and permClass unchanged. 499 */ 500 private void writeObject(ObjectOutputStream out) throws IOException { 501 // Don't call out.defaultWriteObject() 502 503 // Copy perms into a Hashtable 504 Hashtable<String, Permission> permissions = 505 new Hashtable<>(perms.size()*2); 506 507 synchronized (this) { 508 permissions.putAll(perms); 509 } 510 511 // Write out serializable fields 512 ObjectOutputStream.PutField pfields = out.putFields(); 513 pfields.put("all_allowed", all_allowed); 514 pfields.put("permissions", permissions); 515 pfields.put("permClass", permClass); 516 out.writeFields(); 517 } 518 519 /** 520 * readObject is called to restore the state of the 521 * BasicPermissionCollection from a stream. 522 */ 523 private void readObject(java.io.ObjectInputStream in) 524 throws IOException, ClassNotFoundException 525 { 526 // Don't call defaultReadObject() 527 528 // Read in serialized fields 529 ObjectInputStream.GetField gfields = in.readFields(); 530 531 // Get permissions 532 // writeObject writes a Hashtable<String, Permission> for the 533 // permissions key, so this cast is safe, unless the data is corrupt. 534 @SuppressWarnings("unchecked") 535 Hashtable<String, Permission> permissions = 536 (Hashtable<String, Permission>)gfields.get("permissions", null); 537 perms = new HashMap<String, Permission>(permissions.size()*2); 538 perms.putAll(permissions); 539 540 // Get all_allowed 541 all_allowed = gfields.get("all_allowed", false); 542 543 // Get permClass 544 permClass = (Class<?>) gfields.get("permClass", null); 545 546 if (permClass == null) { 547 // set permClass 548 Enumeration<Permission> e = permissions.elements(); 549 if (e.hasMoreElements()) { 550 Permission p = e.nextElement(); 551 permClass = p.getClass(); 552 } 553 } 554 } 555 }