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 }
666