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 @java.io.Serial 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 @java.io.Serial 397 private synchronized void writeObject(java.io.ObjectOutputStream s) 398 throws IOException 399 { 400 // Write out the actions. The superclass takes care of the name 401 // call getActions to make sure actions field is initialized 402 if (actions == null) 403 getActions(); 404 s.defaultWriteObject(); 405 } 406 407 /** 408 * readObject is called to restore the state of the PropertyPermission from 409 * a stream. 410 */ 411 @java.io.Serial 412 private synchronized void readObject(java.io.ObjectInputStream s) 413 throws IOException, ClassNotFoundException 414 { 415 // Read in the action, then initialize the rest 416 s.defaultReadObject(); 417 init(getMask(actions)); 418 } 419 } 420 421 /** 422 * A PropertyPermissionCollection stores a set of PropertyPermission 423 * permissions. 424 * 425 * @see java.security.Permission 426 * @see java.security.Permissions 427 * @see java.security.PermissionCollection 428 * 429 * 430 * @author Roland Schemers 431 * 432 * @serial include 433 */ 434 final class PropertyPermissionCollection extends PermissionCollection 435 implements Serializable 436 { 437 438 /** 439 * Key is property name; value is PropertyPermission. 440 * Not serialized; see serialization section at end of class. 441 */ 442 private transient ConcurrentHashMap<String, PropertyPermission> perms; 443 444 /** 445 * Boolean saying if "*" is in the collection. 446 * 447 * @see #serialPersistentFields 448 */ 449 // No sync access; OK for this to be stale. 450 private boolean all_allowed; 451 452 /** 453 * Create an empty PropertyPermissionCollection object. 454 */ 455 public PropertyPermissionCollection() { 456 perms = new ConcurrentHashMap<>(32); // Capacity for default policy 457 all_allowed = false; 458 } 459 460 /** 461 * Adds a permission to the PropertyPermissions. The key for the hash is 462 * the name. 463 * 464 * @param permission the Permission object to add. 465 * 466 * @exception IllegalArgumentException - if the permission is not a 467 * PropertyPermission 468 * 469 * @exception SecurityException - if this PropertyPermissionCollection 470 * object has been marked readonly 471 */ 472 @Override 473 public void add(Permission permission) { 474 if (! (permission instanceof PropertyPermission)) 475 throw new IllegalArgumentException("invalid permission: "+ 476 permission); 477 if (isReadOnly()) 478 throw new SecurityException( 479 "attempt to add a Permission to a readonly PermissionCollection"); 480 481 PropertyPermission pp = (PropertyPermission) permission; 482 String propName = pp.getName(); 483 484 // Add permission to map if it is absent, or replace with new 485 // permission if applicable. NOTE: cannot use lambda for 486 // remappingFunction parameter until JDK-8076596 is fixed. 487 perms.merge(propName, pp, 488 new java.util.function.BiFunction<>() { 489 @Override 490 public PropertyPermission apply(PropertyPermission existingVal, 491 PropertyPermission newVal) { 492 493 int oldMask = existingVal.getMask(); 494 int newMask = newVal.getMask(); 495 if (oldMask != newMask) { 496 int effective = oldMask | newMask; 497 if (effective == newMask) { 498 return newVal; 499 } 500 if (effective != oldMask) { 501 return new PropertyPermission(propName, effective); 502 } 503 } 504 return existingVal; 505 } 506 } 507 ); 508 509 if (!all_allowed) { 510 if (propName.equals("*")) 511 all_allowed = true; 512 } 513 } 514 515 /** 516 * Check and see if this set of permissions implies the permissions 517 * expressed in "permission". 518 * 519 * @param permission the Permission object to compare 520 * 521 * @return true if "permission" is a proper subset of a permission in 522 * the set, false if not. 523 */ 524 @Override 525 public boolean implies(Permission permission) { 526 if (! (permission instanceof PropertyPermission)) 527 return false; 528 529 PropertyPermission pp = (PropertyPermission) permission; 530 PropertyPermission x; 531 532 int desired = pp.getMask(); 533 int effective = 0; 534 535 // short circuit if the "*" Permission was added 536 if (all_allowed) { 537 x = perms.get("*"); 538 if (x != null) { 539 effective |= x.getMask(); 540 if ((effective & desired) == desired) 541 return true; 542 } 543 } 544 545 // strategy: 546 // Check for full match first. Then work our way up the 547 // name looking for matches on a.b.* 548 549 String name = pp.getName(); 550 //System.out.println("check "+name); 551 552 x = perms.get(name); 553 554 if (x != null) { 555 // we have a direct hit! 556 effective |= x.getMask(); 557 if ((effective & desired) == desired) 558 return true; 559 } 560 561 // work our way up the tree... 562 int last, offset; 563 564 offset = name.length()-1; 565 566 while ((last = name.lastIndexOf('.', offset)) != -1) { 567 568 name = name.substring(0, last+1) + "*"; 569 //System.out.println("check "+name); 570 x = perms.get(name); 571 572 if (x != null) { 573 effective |= x.getMask(); 574 if ((effective & desired) == desired) 575 return true; 576 } 577 offset = last -1; 578 } 579 580 // we don't have to check for "*" as it was already checked 581 // at the top (all_allowed), so we just return false 582 return false; 583 } 584 585 /** 586 * Returns an enumeration of all the PropertyPermission objects in the 587 * container. 588 * 589 * @return an enumeration of all the PropertyPermission objects. 590 */ 591 @Override 592 @SuppressWarnings("unchecked") 593 public Enumeration<Permission> elements() { 594 /** 595 * Casting to rawtype since Enumeration<PropertyPermission> 596 * cannot be directly cast to Enumeration<Permission> 597 */ 598 return (Enumeration)perms.elements(); 599 } 600 601 @java.io.Serial 602 private static final long serialVersionUID = 7015263904581634791L; 603 604 // Need to maintain serialization interoperability with earlier releases, 605 // which had the serializable field: 606 // 607 // Table of permissions. 608 // 609 // @serial 610 // 611 // private Hashtable permissions; 612 /** 613 * @serialField permissions java.util.Hashtable 614 * A table of the PropertyPermissions. 615 * @serialField all_allowed boolean 616 * boolean saying if "*" is in the collection. 617 */ 618 private static final ObjectStreamField[] serialPersistentFields = { 619 new ObjectStreamField("permissions", Hashtable.class), 620 new ObjectStreamField("all_allowed", Boolean.TYPE), 621 }; 622 623 /** 624 * @serialData Default fields. 625 */ 626 /* 627 * Writes the contents of the perms field out as a Hashtable for 628 * serialization compatibility with earlier releases. all_allowed 629 * unchanged. 630 */ 631 @java.io.Serial 632 private void writeObject(ObjectOutputStream out) throws IOException { 633 // Don't call out.defaultWriteObject() 634 635 // Copy perms into a Hashtable 636 Hashtable<String, Permission> permissions = 637 new Hashtable<>(perms.size()*2); 638 permissions.putAll(perms); 639 640 // Write out serializable fields 641 ObjectOutputStream.PutField pfields = out.putFields(); 642 pfields.put("all_allowed", all_allowed); 643 pfields.put("permissions", permissions); 644 out.writeFields(); 645 } 646 647 /* 648 * Reads in a Hashtable of PropertyPermissions and saves them in the 649 * perms field. Reads in all_allowed. 650 */ 651 @java.io.Serial 652 private void readObject(ObjectInputStream in) 653 throws IOException, ClassNotFoundException 654 { 655 // Don't call defaultReadObject() 656 657 // Read in serialized fields 658 ObjectInputStream.GetField gfields = in.readFields(); 659 660 // Get all_allowed 661 all_allowed = gfields.get("all_allowed", false); 662 663 // Get permissions 664 @SuppressWarnings("unchecked") 665 Hashtable<String, PropertyPermission> permissions = 666 (Hashtable<String, PropertyPermission>)gfields.get("permissions", null); 667 perms = new ConcurrentHashMap<>(permissions.size()*2); 668 perms.putAll(permissions); 669 } 670 }