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