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