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