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.util; 27 28 import java.io.Serializable; 29 import java.io.IOException; 30 import java.security.*; 31 import java.util.Map; 32 import java.util.HashMap; 33 import java.util.Enumeration; 34 import java.util.Hashtable; 35 import java.util.Collections; 36 import java.io.ObjectStreamField; 37 import java.io.ObjectOutputStream; 38 import java.io.ObjectInputStream; 39 import java.io.IOException; 40 import sun.security.util.SecurityConstants; 41 42 /** 43 * This class is for property permissions. 44 * 45 * <P> 46 * The name is the name of the property ("java.home", 47 * "os.name", etc). The naming 48 * convention follows the hierarchical property naming convention. 49 * Also, an asterisk 50 * may appear at the end of the name, following a ".", or by itself, to 51 * signify a wildcard match. For example: "java.*" and "*" signify a wildcard 52 * match, while "*java" and "a*b" do not. 53 * <P> 54 * The actions to be granted are passed to the constructor in a string containing 55 * a list of one or more comma-separated keywords. The possible keywords are 56 * "read" and "write". Their meaning is defined as follows: 57 * 58 * <DL> 59 * <DT> read 60 * <DD> read permission. Allows <code>System.getProperty</code> to 61 * be called. 62 * <DT> write 63 * <DD> write permission. Allows <code>System.setProperty</code> to 64 * be called. 65 * </DL> 66 * <P> 67 * The actions string is converted to lowercase before processing. 68 * <P> 69 * Care should be taken before granting code permission to access 70 * certain system properties. For example, granting permission to 71 * access the "java.home" system property gives potentially malevolent 72 * code sensitive information about the system environment (the Java 73 * installation directory). Also, granting permission to access 74 * the "user.name" and "user.home" system properties gives potentially 75 * malevolent code sensitive information about the user environment 76 * (the user's account name and home directory). 77 * 78 * @see java.security.BasicPermission 79 * @see java.security.Permission 80 * @see java.security.Permissions 81 * @see java.security.PermissionCollection 82 * @see java.lang.SecurityManager 83 * 84 * 85 * @author Roland Schemers 86 * @since 1.2 87 * 88 * @serial exclude 89 */ 90 91 public final class PropertyPermission extends BasicPermission { 92 93 /** 94 * Read action. 95 */ 96 private final static int READ = 0x1; 97 98 /** 99 * Write action. 100 */ 101 private final static int WRITE = 0x2; 102 /** 103 * All actions (read,write); 104 */ 105 private final static int ALL = READ|WRITE; 106 /** 107 * No actions. 108 */ 109 private final static int NONE = 0x0; 110 111 /** 112 * The actions mask. 113 * 114 */ 115 private transient int mask; 116 117 /** 118 * The actions string. 119 * 120 * @serial 121 */ 122 private String actions; // Left null as long as possible, then 123 // created and re-used in the getAction function. 124 125 /** 126 * initialize a PropertyPermission object. Common to all constructors. 127 * Also called during de-serialization. 128 * 129 * @param mask the actions mask to use. 130 * 131 */ init(int mask)132 private void init(int mask) { 133 if ((mask & ALL) != mask) 134 throw new IllegalArgumentException("invalid actions mask"); 135 136 if (mask == NONE) 137 throw new IllegalArgumentException("invalid actions mask"); 138 139 if (getName() == null) 140 throw new NullPointerException("name can't be null"); 141 142 this.mask = mask; 143 } 144 145 /** 146 * Creates a new PropertyPermission object with the specified name. 147 * The name is the name of the system property, and 148 * <i>actions</i> contains a comma-separated list of the 149 * desired actions granted on the property. Possible actions are 150 * "read" and "write". 151 * 152 * @param name the name of the PropertyPermission. 153 * @param actions the actions string. 154 * 155 * @throws NullPointerException if <code>name</code> is <code>null</code>. 156 * @throws IllegalArgumentException if <code>name</code> is empty or if 157 * <code>actions</code> is invalid. 158 */ PropertyPermission(String name, String actions)159 public PropertyPermission(String name, String actions) { 160 super(name,actions); 161 init(getMask(actions)); 162 } 163 164 /** 165 * Checks if this PropertyPermission object "implies" the specified 166 * permission. 167 * <P> 168 * More specifically, this method returns true if: 169 * <ul> 170 * <li> <i>p</i> is an instanceof PropertyPermission, 171 * <li> <i>p</i>'s actions are a subset of this 172 * object's actions, and 173 * <li> <i>p</i>'s name is implied by this object's 174 * name. For example, "java.*" implies "java.home". 175 * </ul> 176 * @param p the permission to check against. 177 * 178 * @return true if the specified permission is implied by this object, 179 * false if not. 180 */ implies(Permission p)181 public boolean implies(Permission p) { 182 if (!(p instanceof PropertyPermission)) 183 return false; 184 185 PropertyPermission that = (PropertyPermission) p; 186 187 // we get the effective mask. i.e., the "and" of this and that. 188 // They must be equal to that.mask for implies to return true. 189 190 return ((this.mask & that.mask) == that.mask) && super.implies(that); 191 } 192 193 /** 194 * Checks two PropertyPermission objects for equality. Checks that <i>obj</i> is 195 * a PropertyPermission, and has the same name and actions as this object. 196 * <P> 197 * @param obj the object we are testing for equality with this object. 198 * @return true if obj is a PropertyPermission, and has the same name and 199 * actions as this PropertyPermission object. 200 */ equals(Object obj)201 public boolean equals(Object obj) { 202 if (obj == this) 203 return true; 204 205 if (! (obj instanceof PropertyPermission)) 206 return false; 207 208 PropertyPermission that = (PropertyPermission) obj; 209 210 return (this.mask == that.mask) && 211 (this.getName().equals(that.getName())); 212 } 213 214 /** 215 * Returns the hash code value for this object. 216 * The hash code used is the hash code of this permissions name, that is, 217 * <code>getName().hashCode()</code>, where <code>getName</code> is 218 * from the Permission superclass. 219 * 220 * @return a hash code value for this object. 221 */ hashCode()222 public int hashCode() { 223 return this.getName().hashCode(); 224 } 225 226 /** 227 * Converts an actions String to an actions mask. 228 * 229 * @param actions the action string. 230 * @return the actions mask. 231 */ getMask(String actions)232 private static int getMask(String actions) { 233 234 int mask = NONE; 235 236 if (actions == null) { 237 return mask; 238 } 239 240 // Use object identity comparison against known-interned strings for 241 // performance benefit (these values are used heavily within the JDK). 242 if (actions == SecurityConstants.PROPERTY_READ_ACTION) { 243 return READ; 244 } if (actions == SecurityConstants.PROPERTY_WRITE_ACTION) { 245 return WRITE; 246 } else if (actions == SecurityConstants.PROPERTY_RW_ACTION) { 247 return READ|WRITE; 248 } 249 250 char[] a = actions.toCharArray(); 251 252 int i = a.length - 1; 253 if (i < 0) 254 return mask; 255 256 while (i != -1) { 257 char c; 258 259 // skip whitespace 260 while ((i!=-1) && ((c = a[i]) == ' ' || 261 c == '\r' || 262 c == '\n' || 263 c == '\f' || 264 c == '\t')) 265 i--; 266 267 // check for the known strings 268 int matchlen; 269 270 if (i >= 3 && (a[i-3] == 'r' || a[i-3] == 'R') && 271 (a[i-2] == 'e' || a[i-2] == 'E') && 272 (a[i-1] == 'a' || a[i-1] == 'A') && 273 (a[i] == 'd' || a[i] == 'D')) 274 { 275 matchlen = 4; 276 mask |= READ; 277 278 } else if (i >= 4 && (a[i-4] == 'w' || a[i-4] == 'W') && 279 (a[i-3] == 'r' || a[i-3] == 'R') && 280 (a[i-2] == 'i' || a[i-2] == 'I') && 281 (a[i-1] == 't' || a[i-1] == 'T') && 282 (a[i] == 'e' || a[i] == 'E')) 283 { 284 matchlen = 5; 285 mask |= WRITE; 286 287 } else { 288 // parse error 289 throw new IllegalArgumentException( 290 "invalid permission: " + actions); 291 } 292 293 // make sure we didn't just match the tail of a word 294 // like "ackbarfaccept". Also, skip to the comma. 295 boolean seencomma = false; 296 while (i >= matchlen && !seencomma) { 297 switch(a[i-matchlen]) { 298 case ',': 299 seencomma = true; 300 break; 301 case ' ': case '\r': case '\n': 302 case '\f': case '\t': 303 break; 304 default: 305 throw new IllegalArgumentException( 306 "invalid permission: " + actions); 307 } 308 i--; 309 } 310 311 // point i at the location of the comma minus one (or -1). 312 i -= matchlen; 313 } 314 315 return mask; 316 } 317 318 319 /** 320 * Return the canonical string representation of the actions. 321 * Always returns present actions in the following order: 322 * read, write. 323 * 324 * @return the canonical string representation of the actions. 325 */ getActions(int mask)326 static String getActions(int mask) { 327 StringBuilder sb = new StringBuilder(); 328 boolean comma = false; 329 330 if ((mask & READ) == READ) { 331 comma = true; 332 sb.append("read"); 333 } 334 335 if ((mask & WRITE) == WRITE) { 336 if (comma) sb.append(','); 337 else comma = true; 338 sb.append("write"); 339 } 340 return sb.toString(); 341 } 342 343 /** 344 * Returns the "canonical string representation" of the actions. 345 * That is, this method always returns present actions in the following order: 346 * read, write. For example, if this PropertyPermission object 347 * allows both write and read actions, a call to <code>getActions</code> 348 * will return the string "read,write". 349 * 350 * @return the canonical string representation of the actions. 351 */ getActions()352 public String getActions() { 353 if (actions == null) 354 actions = getActions(this.mask); 355 356 return actions; 357 } 358 359 /** 360 * Return the current action mask. 361 * Used by the PropertyPermissionCollection 362 * 363 * @return the actions mask. 364 */ getMask()365 int getMask() { 366 return mask; 367 } 368 369 /** 370 * Returns a new PermissionCollection object for storing 371 * PropertyPermission objects. 372 * <p> 373 * 374 * @return a new PermissionCollection object suitable for storing 375 * PropertyPermissions. 376 */ newPermissionCollection()377 public PermissionCollection newPermissionCollection() { 378 return new PropertyPermissionCollection(); 379 } 380 381 382 private static final long serialVersionUID = 885438825399942851L; 383 384 /** 385 * WriteObject is called to save the state of the PropertyPermission 386 * to a stream. The actions are serialized, and the superclass 387 * takes care of the name. 388 */ writeObject(java.io.ObjectOutputStream s)389 private synchronized void writeObject(java.io.ObjectOutputStream s) 390 throws IOException 391 { 392 // Write out the actions. The superclass takes care of the name 393 // call getActions to make sure actions field is initialized 394 if (actions == null) 395 getActions(); 396 s.defaultWriteObject(); 397 } 398 399 /** 400 * readObject is called to restore the state of the PropertyPermission from 401 * a stream. 402 */ readObject(java.io.ObjectInputStream s)403 private synchronized void readObject(java.io.ObjectInputStream s) 404 throws IOException, ClassNotFoundException 405 { 406 // Read in the action, then initialize the rest 407 s.defaultReadObject(); 408 init(getMask(actions)); 409 } 410 } 411 412 /** 413 * A PropertyPermissionCollection stores a set of PropertyPermission 414 * permissions. 415 * 416 * @see java.security.Permission 417 * @see java.security.Permissions 418 * @see java.security.PermissionCollection 419 * 420 * 421 * @author Roland Schemers 422 * 423 * @serial include 424 */ 425 final class PropertyPermissionCollection extends PermissionCollection 426 implements Serializable 427 { 428 429 /** 430 * Key is property name; value is PropertyPermission. 431 * Not serialized; see serialization section at end of class. 432 */ 433 private transient Map<String, PropertyPermission> perms; 434 435 /** 436 * Boolean saying if "*" is in the collection. 437 * 438 * @see #serialPersistentFields 439 */ 440 // No sync access; OK for this to be stale. 441 private boolean all_allowed; 442 443 /** 444 * Create an empty PropertyPermissionCollection object. 445 */ PropertyPermissionCollection()446 public PropertyPermissionCollection() { 447 perms = new HashMap<>(32); // Capacity for default policy 448 all_allowed = false; 449 } 450 451 /** 452 * Adds a permission to the PropertyPermissions. The key for the hash is 453 * the name. 454 * 455 * @param permission the Permission object to add. 456 * 457 * @exception IllegalArgumentException - if the permission is not a 458 * PropertyPermission 459 * 460 * @exception SecurityException - if this PropertyPermissionCollection 461 * object has been marked readonly 462 */ add(Permission permission)463 public void add(Permission permission) { 464 if (! (permission instanceof PropertyPermission)) 465 throw new IllegalArgumentException("invalid permission: "+ 466 permission); 467 if (isReadOnly()) 468 throw new SecurityException( 469 "attempt to add a Permission to a readonly PermissionCollection"); 470 471 PropertyPermission pp = (PropertyPermission) permission; 472 String propName = pp.getName(); 473 474 synchronized (this) { 475 PropertyPermission existing = perms.get(propName); 476 477 if (existing != null) { 478 int oldMask = existing.getMask(); 479 int newMask = pp.getMask(); 480 if (oldMask != newMask) { 481 int effective = oldMask | newMask; 482 String actions = PropertyPermission.getActions(effective); 483 perms.put(propName, new PropertyPermission(propName, actions)); 484 } 485 } else { 486 perms.put(propName, pp); 487 } 488 } 489 490 if (!all_allowed) { 491 if (propName.equals("*")) 492 all_allowed = true; 493 } 494 } 495 496 /** 497 * Check and see if this set of permissions implies the permissions 498 * expressed in "permission". 499 * 500 * @param permission the Permission object to compare 501 * 502 * @return true if "permission" is a proper subset of a permission in 503 * the set, false if not. 504 */ implies(Permission permission)505 public boolean implies(Permission permission) { 506 if (! (permission instanceof PropertyPermission)) 507 return false; 508 509 PropertyPermission pp = (PropertyPermission) permission; 510 PropertyPermission x; 511 512 int desired = pp.getMask(); 513 int effective = 0; 514 515 // short circuit if the "*" Permission was added 516 if (all_allowed) { 517 synchronized (this) { 518 x = perms.get("*"); 519 } 520 if (x != null) { 521 effective |= x.getMask(); 522 if ((effective & desired) == desired) 523 return true; 524 } 525 } 526 527 // strategy: 528 // Check for full match first. Then work our way up the 529 // name looking for matches on a.b.* 530 531 String name = pp.getName(); 532 //System.out.println("check "+name); 533 534 synchronized (this) { 535 x = perms.get(name); 536 } 537 538 if (x != null) { 539 // we have a direct hit! 540 effective |= x.getMask(); 541 if ((effective & desired) == desired) 542 return true; 543 } 544 545 // work our way up the tree... 546 int last, offset; 547 548 offset = name.length()-1; 549 550 while ((last = name.lastIndexOf(".", offset)) != -1) { 551 552 name = name.substring(0, last+1) + "*"; 553 //System.out.println("check "+name); 554 synchronized (this) { 555 x = perms.get(name); 556 } 557 558 if (x != null) { 559 effective |= x.getMask(); 560 if ((effective & desired) == desired) 561 return true; 562 } 563 offset = last -1; 564 } 565 566 // we don't have to check for "*" as it was already checked 567 // at the top (all_allowed), so we just return false 568 return false; 569 } 570 571 /** 572 * Returns an enumeration of all the PropertyPermission objects in the 573 * container. 574 * 575 * @return an enumeration of all the PropertyPermission objects. 576 */ 577 @SuppressWarnings("unchecked") elements()578 public Enumeration<Permission> elements() { 579 // Convert Iterator of Map values into an Enumeration 580 synchronized (this) { 581 /** 582 * Casting to rawtype since Enumeration<PropertyPermission> 583 * cannot be directly cast to Enumeration<Permission> 584 */ 585 return (Enumeration)Collections.enumeration(perms.values()); 586 } 587 } 588 589 private static final long serialVersionUID = 7015263904581634791L; 590 591 // Need to maintain serialization interoperability with earlier releases, 592 // which had the serializable field: 593 // 594 // Table of permissions. 595 // 596 // @serial 597 // 598 // private Hashtable permissions; 599 /** 600 * @serialField permissions java.util.Hashtable 601 * A table of the PropertyPermissions. 602 * @serialField all_allowed boolean 603 * boolean saying if "*" is in the collection. 604 */ 605 private static final ObjectStreamField[] serialPersistentFields = { 606 new ObjectStreamField("permissions", Hashtable.class), 607 new ObjectStreamField("all_allowed", Boolean.TYPE), 608 }; 609 610 /** 611 * @serialData Default fields. 612 */ 613 /* 614 * Writes the contents of the perms field out as a Hashtable for 615 * serialization compatibility with earlier releases. all_allowed 616 * unchanged. 617 */ writeObject(ObjectOutputStream out)618 private void writeObject(ObjectOutputStream out) throws IOException { 619 // Don't call out.defaultWriteObject() 620 621 // Copy perms into a Hashtable 622 Hashtable<String, Permission> permissions = 623 new Hashtable<>(perms.size()*2); 624 synchronized (this) { 625 permissions.putAll(perms); 626 } 627 628 // Write out serializable fields 629 ObjectOutputStream.PutField pfields = out.putFields(); 630 pfields.put("all_allowed", all_allowed); 631 pfields.put("permissions", permissions); 632 out.writeFields(); 633 } 634 635 /* 636 * Reads in a Hashtable of PropertyPermissions and saves them in the 637 * perms field. Reads in all_allowed. 638 */ readObject(ObjectInputStream in)639 private void readObject(ObjectInputStream in) 640 throws IOException, ClassNotFoundException 641 { 642 // Don't call defaultReadObject() 643 644 // Read in serialized fields 645 ObjectInputStream.GetField gfields = in.readFields(); 646 647 // Get all_allowed 648 all_allowed = gfields.get("all_allowed", false); 649 650 // Get permissions 651 @SuppressWarnings("unchecked") 652 Hashtable<String, PropertyPermission> permissions = 653 (Hashtable<String, PropertyPermission>)gfields.get("permissions", null); 654 perms = new HashMap<>(permissions.size()*2); 655 perms.putAll(permissions); 656 } 657 } 658