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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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")
 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      */
 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      */
 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 }