View Javadoc
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.security;
27  
28  import java.util.Enumeration;
29  import java.util.Map;
30  import java.util.HashMap;
31  import java.util.Hashtable;
32  import java.util.Collections;
33  import java.io.ObjectStreamField;
34  import java.io.ObjectOutputStream;
35  import java.io.ObjectInputStream;
36  import java.io.IOException;
37  
38  /**
39   * The BasicPermission class extends the Permission class, and
40   * can be used as the base class for permissions that want to
41   * follow the same naming convention as BasicPermission.
42   * <P>
43   * The name for a BasicPermission is the name of the given permission
44   * (for example, "exit",
45   * "setFactory", "print.queueJob", etc). The naming
46   * convention follows the  hierarchical property naming convention.
47   * An asterisk may appear by itself, or if immediately preceded by a "."
48   * may appear at the end of the name, to signify a wildcard match.
49   * For example, "*" and "java.*" signify a wildcard match, while "*java", "a*b",
50   * and "java*" do not.
51   * <P>
52   * The action string (inherited from Permission) is unused.
53   * Thus, BasicPermission is commonly used as the base class for
54   * "named" permissions
55   * (ones that contain a name but no actions list; you either have the
56   * named permission or you don't.)
57   * Subclasses may implement actions on top of BasicPermission,
58   * if desired.
59   * <p>
60   * @see java.security.Permission
61   * @see java.security.Permissions
62   * @see java.security.PermissionCollection
63   * @see java.lang.SecurityManager
64   *
65   * @author Marianne Mueller
66   * @author Roland Schemers
67   */
68  
69  public abstract class BasicPermission extends Permission
70      implements java.io.Serializable
71  {
72  
73      private static final long serialVersionUID = 6279438298436773498L;
74  
75      // does this permission have a wildcard at the end?
76      private transient boolean wildcard;
77  
78      // the name without the wildcard on the end
79      private transient String path;
80  
81      // is this permission the old-style exitVM permission (pre JDK 1.6)?
82      private transient boolean exitVM;
83  
84      /**
85       * initialize a BasicPermission object. Common to all constructors.
86       */
87      private void init(String name) {
88          if (name == null)
89              throw new NullPointerException("name can't be null");
90  
91          int len = name.length();
92  
93          if (len == 0) {
94              throw new IllegalArgumentException("name can't be empty");
95          }
96  
97          char last = name.charAt(len - 1);
98  
99          // Is wildcard or ends with ".*"?
100         if (last == '*' && (len == 1 || name.charAt(len - 2) == '.')) {
101             wildcard = true;
102             if (len == 1) {
103                 path = "";
104             } else {
105                 path = name.substring(0, len - 1);
106             }
107         } else {
108             if (name.equals("exitVM")) {
109                 wildcard = true;
110                 path = "exitVM.";
111                 exitVM = true;
112             } else {
113                 path = name;
114             }
115         }
116     }
117 
118     /**
119      * Creates a new BasicPermission with the specified name.
120      * Name is the symbolic name of the permission, such as
121      * "setFactory",
122      * "print.queueJob", or "topLevelWindow", etc.
123      *
124      * @param name the name of the BasicPermission.
125      *
126      * @throws NullPointerException if {@code name} is {@code null}.
127      * @throws IllegalArgumentException if {@code name} is empty.
128      */
129     public BasicPermission(String name) {
130         super(name);
131         init(name);
132     }
133 
134 
135     /**
136      * Creates a new BasicPermission object with the specified name.
137      * The name is the symbolic name of the BasicPermission, and the
138      * actions String is currently unused.
139      *
140      * @param name the name of the BasicPermission.
141      * @param actions ignored.
142      *
143      * @throws NullPointerException if {@code name} is {@code null}.
144      * @throws IllegalArgumentException if {@code name} is empty.
145      */
146     public BasicPermission(String name, String actions) {
147         super(name);
148         init(name);
149     }
150 
151     /**
152      * Checks if the specified permission is "implied" by
153      * this object.
154      * <P>
155      * More specifically, this method returns true if:
156      * <ul>
157      * <li> <i>p</i>'s class is the same as this object's class, and
158      * <li> <i>p</i>'s name equals or (in the case of wildcards)
159      *      is implied by this object's
160      *      name. For example, "a.b.*" implies "a.b.c".
161      * </ul>
162      *
163      * @param p the permission to check against.
164      *
165      * @return true if the passed permission is equal to or
166      * implied by this permission, false otherwise.
167      */
168     public boolean implies(Permission p) {
169         if ((p == null) || (p.getClass() != getClass()))
170             return false;
171 
172         BasicPermission that = (BasicPermission) p;
173 
174         if (this.wildcard) {
175             if (that.wildcard) {
176                 // one wildcard can imply another
177                 return that.path.startsWith(path);
178             } else {
179                 // make sure ap.path is longer so a.b.* doesn't imply a.b
180                 return (that.path.length() > this.path.length()) &&
181                     that.path.startsWith(this.path);
182             }
183         } else {
184             if (that.wildcard) {
185                 // a non-wildcard can't imply a wildcard
186                 return false;
187             }
188             else {
189                 return this.path.equals(that.path);
190             }
191         }
192     }
193 
194     /**
195      * Checks two BasicPermission objects for equality.
196      * Checks that <i>obj</i>'s class is the same as this object's class
197      * and has the same name as this object.
198      * <P>
199      * @param obj the object we are testing for equality with this object.
200      * @return true if <i>obj</i>'s class is the same as this object's class
201      *  and has the same name as this BasicPermission object, false otherwise.
202      */
203     public boolean equals(Object obj) {
204         if (obj == this)
205             return true;
206 
207         if ((obj == null) || (obj.getClass() != getClass()))
208             return false;
209 
210         BasicPermission bp = (BasicPermission) obj;
211 
212         return getName().equals(bp.getName());
213     }
214 
215 
216     /**
217      * Returns the hash code value for this object.
218      * The hash code used is the hash code of the name, that is,
219      * {@code getName().hashCode()}, where {@code getName} is
220      * from the Permission superclass.
221      *
222      * @return a hash code value for this object.
223      */
224     public int hashCode() {
225         return this.getName().hashCode();
226     }
227 
228     /**
229      * Returns the canonical string representation of the actions,
230      * which currently is the empty string "", since there are no actions for
231      * a BasicPermission.
232      *
233      * @return the empty string "".
234      */
235     public String getActions() {
236         return "";
237     }
238 
239     /**
240      * Returns a new PermissionCollection object for storing BasicPermission
241      * objects.
242      *
243      * <p>BasicPermission objects must be stored in a manner that allows them
244      * to be inserted in any order, but that also enables the
245      * PermissionCollection {@code implies} method
246      * to be implemented in an efficient (and consistent) manner.
247      *
248      * @return a new PermissionCollection object suitable for
249      * storing BasicPermissions.
250      */
251     public PermissionCollection newPermissionCollection() {
252         return new BasicPermissionCollection(this.getClass());
253     }
254 
255     /**
256      * readObject is called to restore the state of the BasicPermission from
257      * a stream.
258      */
259     private void readObject(ObjectInputStream s)
260          throws IOException, ClassNotFoundException
261     {
262         s.defaultReadObject();
263         // init is called to initialize the rest of the values.
264         init(getName());
265     }
266 
267     /**
268      * Returns the canonical name of this BasicPermission.
269      * All internal invocations of getName should invoke this method, so
270      * that the pre-JDK 1.6 "exitVM" and current "exitVM.*" permission are
271      * equivalent in equals/hashCode methods.
272      *
273      * @return the canonical name of this BasicPermission.
274      */
275     final String getCanonicalName() {
276         return exitVM ? "exitVM.*" : getName();
277     }
278 }
279 
280 /**
281  * A BasicPermissionCollection stores a collection
282  * of BasicPermission permissions. BasicPermission objects
283  * must be stored in a manner that allows them to be inserted in any
284  * order, but enable the implies function to evaluate the implies
285  * method in an efficient (and consistent) manner.
286  *
287  * A BasicPermissionCollection handles comparing a permission like "a.b.c.d.e"
288  * with a Permission such as "a.b.*", or "*".
289  *
290  * @see java.security.Permission
291  * @see java.security.Permissions
292  *
293  *
294  * @author Roland Schemers
295  *
296  * @serial include
297  */
298 
299 final class BasicPermissionCollection
300     extends PermissionCollection
301     implements java.io.Serializable
302 {
303 
304     private static final long serialVersionUID = 739301742472979399L;
305 
306     /**
307       * Key is name, value is permission. All permission objects in
308       * collection must be of the same type.
309       * Not serialized; see serialization section at end of class.
310       */
311     private transient Map<String, Permission> perms;
312 
313     /**
314      * This is set to {@code true} if this BasicPermissionCollection
315      * contains a BasicPermission with '*' as its permission name.
316      *
317      * @see #serialPersistentFields
318      */
319     private boolean all_allowed;
320 
321     /**
322      * The class to which all BasicPermissions in this
323      * BasicPermissionCollection belongs.
324      *
325      * @see #serialPersistentFields
326      */
327     private Class<?> permClass;
328 
329     /**
330      * Create an empty BasicPermissionCollection object.
331      *
332      */
333 
334     public BasicPermissionCollection(Class<?> clazz) {
335         perms = new HashMap<String, Permission>(11);
336         all_allowed = false;
337         permClass = clazz;
338     }
339 
340     /**
341      * Adds a permission to the BasicPermissions. The key for the hash is
342      * permission.path.
343      *
344      * @param permission the Permission object to add.
345      *
346      * @exception IllegalArgumentException - if the permission is not a
347      *                                       BasicPermission, or if
348      *                                       the permission is not of the
349      *                                       same Class as the other
350      *                                       permissions in this collection.
351      *
352      * @exception SecurityException - if this BasicPermissionCollection object
353      *                                has been marked readonly
354      */
355     public void add(Permission permission) {
356         if (! (permission instanceof BasicPermission))
357             throw new IllegalArgumentException("invalid permission: "+
358                                                permission);
359         if (isReadOnly())
360             throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");
361 
362         BasicPermission bp = (BasicPermission) permission;
363 
364         // make sure we only add new BasicPermissions of the same class
365         // Also check null for compatibility with deserialized form from
366         // previous versions.
367         if (permClass == null) {
368             // adding first permission
369             permClass = bp.getClass();
370         } else {
371             if (bp.getClass() != permClass)
372                 throw new IllegalArgumentException("invalid permission: " +
373                                                 permission);
374         }
375 
376         synchronized (this) {
377             perms.put(bp.getCanonicalName(), permission);
378         }
379 
380         // No sync on all_allowed; staleness OK
381         if (!all_allowed) {
382             if (bp.getCanonicalName().equals("*"))
383                 all_allowed = true;
384         }
385     }
386 
387     /**
388      * Check and see if this set of permissions implies the permissions
389      * expressed in "permission".
390      *
391      * @param permission the Permission object to compare
392      *
393      * @return true if "permission" is a proper subset of a permission in
394      * the set, false if not.
395      */
396     public boolean implies(Permission permission) {
397         if (! (permission instanceof BasicPermission))
398             return false;
399 
400         BasicPermission bp = (BasicPermission) permission;
401 
402         // random subclasses of BasicPermission do not imply each other
403         if (bp.getClass() != permClass)
404             return false;
405 
406         // short circuit if the "*" Permission was added
407         if (all_allowed)
408             return true;
409 
410         // strategy:
411         // Check for full match first. Then work our way up the
412         // path looking for matches on a.b..*
413 
414         String path = bp.getCanonicalName();
415         //System.out.println("check "+path);
416 
417         Permission x;
418 
419         synchronized (this) {
420             x = perms.get(path);
421         }
422 
423         if (x != null) {
424             // we have a direct hit!
425             return x.implies(permission);
426         }
427 
428         // work our way up the tree...
429         int last, offset;
430 
431         offset = path.length()-1;
432 
433         while ((last = path.lastIndexOf(".", offset)) != -1) {
434 
435             path = path.substring(0, last+1) + "*";
436             //System.out.println("check "+path);
437 
438             synchronized (this) {
439                 x = perms.get(path);
440             }
441 
442             if (x != null) {
443                 return x.implies(permission);
444             }
445             offset = last -1;
446         }
447 
448         // we don't have to check for "*" as it was already checked
449         // at the top (all_allowed), so we just return false
450         return false;
451     }
452 
453     /**
454      * Returns an enumeration of all the BasicPermission objects in the
455      * container.
456      *
457      * @return an enumeration of all the BasicPermission objects.
458      */
459     public Enumeration<Permission> elements() {
460         // Convert Iterator of Map values into an Enumeration
461         synchronized (this) {
462             return Collections.enumeration(perms.values());
463         }
464     }
465 
466     // Need to maintain serialization interoperability with earlier releases,
467     // which had the serializable field:
468     //
469     // @serial the Hashtable is indexed by the BasicPermission name
470     //
471     // private Hashtable permissions;
472     /**
473      * @serialField permissions java.util.Hashtable
474      *    The BasicPermissions in this BasicPermissionCollection.
475      *    All BasicPermissions in the collection must belong to the same class.
476      *    The Hashtable is indexed by the BasicPermission name; the value
477      *    of the Hashtable entry is the permission.
478      * @serialField all_allowed boolean
479      *   This is set to {@code true} if this BasicPermissionCollection
480      *   contains a BasicPermission with '*' as its permission name.
481      * @serialField permClass java.lang.Class
482      *   The class to which all BasicPermissions in this
483      *   BasicPermissionCollection belongs.
484      */
485     private static final ObjectStreamField[] serialPersistentFields = {
486         new ObjectStreamField("permissions", Hashtable.class),
487         new ObjectStreamField("all_allowed", Boolean.TYPE),
488         new ObjectStreamField("permClass", Class.class),
489     };
490 
491     /**
492      * @serialData Default fields.
493      */
494     /*
495      * Writes the contents of the perms field out as a Hashtable for
496      * serialization compatibility with earlier releases. all_allowed
497      * and permClass unchanged.
498      */
499     private void writeObject(ObjectOutputStream out) throws IOException {
500         // Don't call out.defaultWriteObject()
501 
502         // Copy perms into a Hashtable
503         Hashtable<String, Permission> permissions =
504                 new Hashtable<>(perms.size()*2);
505 
506         synchronized (this) {
507             permissions.putAll(perms);
508         }
509 
510         // Write out serializable fields
511         ObjectOutputStream.PutField pfields = out.putFields();
512         pfields.put("all_allowed", all_allowed);
513         pfields.put("permissions", permissions);
514         pfields.put("permClass", permClass);
515         out.writeFields();
516     }
517 
518     /**
519      * readObject is called to restore the state of the
520      * BasicPermissionCollection from a stream.
521      */
522     private void readObject(java.io.ObjectInputStream in)
523          throws IOException, ClassNotFoundException
524     {
525         // Don't call defaultReadObject()
526 
527         // Read in serialized fields
528         ObjectInputStream.GetField gfields = in.readFields();
529 
530         // Get permissions
531         // writeObject writes a Hashtable<String, Permission> for the
532         // permissions key, so this cast is safe, unless the data is corrupt.
533         @SuppressWarnings("unchecked")
534         Hashtable<String, Permission> permissions =
535                 (Hashtable<String, Permission>)gfields.get("permissions", null);
536         perms = new HashMap<String, Permission>(permissions.size()*2);
537         perms.putAll(permissions);
538 
539         // Get all_allowed
540         all_allowed = gfields.get("all_allowed", false);
541 
542         // Get permClass
543         permClass = (Class<?>) gfields.get("permClass", null);
544 
545         if (permClass == null) {
546             // set permClass
547             Enumeration<Permission> e = permissions.elements();
548             if (e.hasMoreElements()) {
549                 Permission p = e.nextElement();
550                 permClass = p.getClass();
551             }
552         }
553     }
554 }