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.Hashtable;
30  import java.util.NoSuchElementException;
31  import java.util.Map;
32  import java.util.HashMap;
33  import java.util.List;
34  import java.util.Iterator;
35  import java.util.Collections;
36  import java.io.Serializable;
37  import java.io.ObjectStreamField;
38  import java.io.ObjectOutputStream;
39  import java.io.ObjectInputStream;
40  import java.io.IOException;
41  
42  
43  /**
44   * This class represents a heterogeneous collection of Permissions. That is,
45   * it contains different types of Permission objects, organized into
46   * PermissionCollections. For example, if any
47   * {@code java.io.FilePermission} objects are added to an instance of
48   * this class, they are all stored in a single
49   * PermissionCollection. It is the PermissionCollection returned by a call to
50   * the {@code newPermissionCollection} method in the FilePermission class.
51   * Similarly, any {@code java.lang.RuntimePermission} objects are
52   * stored in the PermissionCollection returned by a call to the
53   * {@code newPermissionCollection} method in the
54   * RuntimePermission class. Thus, this class represents a collection of
55   * PermissionCollections.
56   *
57   * <p>When the {@code add} method is called to add a Permission, the
58   * Permission is stored in the appropriate PermissionCollection. If no such
59   * collection exists yet, the Permission object's class is determined and the
60   * {@code newPermissionCollection} method is called on that class to create
61   * the PermissionCollection and add it to the Permissions object. If
62   * {@code newPermissionCollection} returns null, then a default
63   * PermissionCollection that uses a hashtable will be created and used. Each
64   * hashtable entry stores a Permission object as both the key and the value.
65   *
66   * <p> Enumerations returned via the {@code elements} method are
67   * not <em>fail-fast</em>.  Modifications to a collection should not be
68   * performed while enumerating over that collection.
69   *
70   * @see Permission
71   * @see PermissionCollection
72   * @see AllPermission
73   *
74   *
75   * @author Marianne Mueller
76   * @author Roland Schemers
77   *
78   * @serial exclude
79   */
80  
81  public final class Permissions extends PermissionCollection
82  implements Serializable
83  {
84      /**
85       * Key is permissions Class, value is PermissionCollection for that class.
86       * Not serialized; see serialization section at end of class.
87       */
88      private transient Map<Class<?>, PermissionCollection> permsMap;
89  
90      // optimization. keep track of whether unresolved permissions need to be
91      // checked
92      private transient boolean hasUnresolved = false;
93  
94      // optimization. keep track of the AllPermission collection
95      // - package private for ProtectionDomain optimization
96      PermissionCollection allPermission;
97  
98      /**
99       * Creates a new Permissions object containing no PermissionCollections.
100      */
101     public Permissions() {
102         permsMap = new HashMap<Class<?>, PermissionCollection>(11);
103         allPermission = null;
104     }
105 
106     /**
107      * Adds a permission object to the PermissionCollection for the class the
108      * permission belongs to. For example, if <i>permission</i> is a
109      * FilePermission, it is added to the FilePermissionCollection stored
110      * in this Permissions object.
111      *
112      * This method creates
113      * a new PermissionCollection object (and adds the permission to it)
114      * if an appropriate collection does not yet exist. <p>
115      *
116      * @param permission the Permission object to add.
117      *
118      * @exception SecurityException if this Permissions object is
119      * marked as readonly.
120      *
121      * @see PermissionCollection#isReadOnly()
122      */
123 
124     public void add(Permission permission) {
125         if (isReadOnly())
126             throw new SecurityException(
127               "attempt to add a Permission to a readonly Permissions object");
128 
129         PermissionCollection pc;
130 
131         synchronized (this) {
132             pc = getPermissionCollection(permission, true);
133             pc.add(permission);
134         }
135 
136         // No sync; staleness -> optimizations delayed, which is OK
137         if (permission instanceof AllPermission) {
138             allPermission = pc;
139         }
140         if (permission instanceof UnresolvedPermission) {
141             hasUnresolved = true;
142         }
143     }
144 
145     /**
146      * Checks to see if this object's PermissionCollection for permissions of
147      * the specified permission's class implies the permissions
148      * expressed in the <i>permission</i> object. Returns true if the
149      * combination of permissions in the appropriate PermissionCollection
150      * (e.g., a FilePermissionCollection for a FilePermission) together
151      * imply the specified permission.
152      *
153      * <p>For example, suppose there is a FilePermissionCollection in this
154      * Permissions object, and it contains one FilePermission that specifies
155      * "read" access for  all files in all subdirectories of the "/tmp"
156      * directory, and another FilePermission that specifies "write" access
157      * for all files in the "/tmp/scratch/foo" directory.
158      * Then if the {@code implies} method
159      * is called with a permission specifying both "read" and "write" access
160      * to files in the "/tmp/scratch/foo" directory, {@code true} is
161      * returned.
162      *
163      * <p>Additionally, if this PermissionCollection contains the
164      * AllPermission, this method will always return true.
165      * <p>
166      * @param permission the Permission object to check.
167      *
168      * @return true if "permission" is implied by the permissions in the
169      * PermissionCollection it
170      * belongs to, false if not.
171      */
172 
173     public boolean implies(Permission permission) {
174         // No sync; staleness -> skip optimization, which is OK
175         if (allPermission != null) {
176             return true; // AllPermission has already been added
177         } else {
178             synchronized (this) {
179                 PermissionCollection pc = getPermissionCollection(permission,
180                     false);
181                 if (pc != null) {
182                     return pc.implies(permission);
183                 } else {
184                     // none found
185                     return false;
186                 }
187             }
188         }
189     }
190 
191     /**
192      * Returns an enumeration of all the Permission objects in all the
193      * PermissionCollections in this Permissions object.
194      *
195      * @return an enumeration of all the Permissions.
196      */
197 
198     public Enumeration<Permission> elements() {
199         // go through each Permissions in the hash table
200         // and call their elements() function.
201 
202         synchronized (this) {
203             return new PermissionsEnumerator(permsMap.values().iterator());
204         }
205     }
206 
207     /**
208      * Gets the PermissionCollection in this Permissions object for
209      * permissions whose type is the same as that of <i>p</i>.
210      * For example, if <i>p</i> is a FilePermission,
211      * the FilePermissionCollection
212      * stored in this Permissions object will be returned.
213      *
214      * If createEmpty is true,
215      * this method creates a new PermissionCollection object for the specified
216      * type of permission objects if one does not yet exist.
217      * To do so, it first calls the {@code newPermissionCollection} method
218      * on <i>p</i>.  Subclasses of class Permission
219      * override that method if they need to store their permissions in a
220      * particular PermissionCollection object in order to provide the
221      * correct semantics when the {@code PermissionCollection.implies}
222      * method is called.
223      * If the call returns a PermissionCollection, that collection is stored
224      * in this Permissions object. If the call returns null and createEmpty
225      * is true, then
226      * this method instantiates and stores a default PermissionCollection
227      * that uses a hashtable to store its permission objects.
228      *
229      * createEmpty is ignored when creating empty PermissionCollection
230      * for unresolved permissions because of the overhead of determining the
231      * PermissionCollection to use.
232      *
233      * createEmpty should be set to false when this method is invoked from
234      * implies() because it incurs the additional overhead of creating and
235      * adding an empty PermissionCollection that will just return false.
236      * It should be set to true when invoked from add().
237      */
238     private PermissionCollection getPermissionCollection(Permission p,
239         boolean createEmpty) {
240         Class<?> c = p.getClass();
241 
242         PermissionCollection pc = permsMap.get(c);
243 
244         if (!hasUnresolved && !createEmpty) {
245             return pc;
246         } else if (pc == null) {
247 
248             // Check for unresolved permissions
249             pc = (hasUnresolved ? getUnresolvedPermissions(p) : null);
250 
251             // if still null, create a new collection
252             if (pc == null && createEmpty) {
253 
254                 pc = p.newPermissionCollection();
255 
256                 // still no PermissionCollection?
257                 // We'll give them a PermissionsHash.
258                 if (pc == null)
259                     pc = new PermissionsHash();
260             }
261 
262             if (pc != null) {
263                 permsMap.put(c, pc);
264             }
265         }
266         return pc;
267     }
268 
269     /**
270      * Resolves any unresolved permissions of type p.
271      *
272      * @param p the type of unresolved permission to resolve
273      *
274      * @return PermissionCollection containing the unresolved permissions,
275      *  or null if there were no unresolved permissions of type p.
276      *
277      */
278     private PermissionCollection getUnresolvedPermissions(Permission p)
279     {
280         // Called from within synchronized method so permsMap doesn't need lock
281 
282         UnresolvedPermissionCollection uc =
283         (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class);
284 
285         // we have no unresolved permissions if uc is null
286         if (uc == null)
287             return null;
288 
289         List<UnresolvedPermission> unresolvedPerms =
290                                         uc.getUnresolvedPermissions(p);
291 
292         // we have no unresolved permissions of this type if unresolvedPerms is null
293         if (unresolvedPerms == null)
294             return null;
295 
296         java.security.cert.Certificate certs[] = null;
297 
298         Object signers[] = p.getClass().getSigners();
299 
300         int n = 0;
301         if (signers != null) {
302             for (int j=0; j < signers.length; j++) {
303                 if (signers[j] instanceof java.security.cert.Certificate) {
304                     n++;
305                 }
306             }
307             certs = new java.security.cert.Certificate[n];
308             n = 0;
309             for (int j=0; j < signers.length; j++) {
310                 if (signers[j] instanceof java.security.cert.Certificate) {
311                     certs[n++] = (java.security.cert.Certificate)signers[j];
312                 }
313             }
314         }
315 
316         PermissionCollection pc = null;
317         synchronized (unresolvedPerms) {
318             int len = unresolvedPerms.size();
319             for (int i = 0; i < len; i++) {
320                 UnresolvedPermission up = unresolvedPerms.get(i);
321                 Permission perm = up.resolve(p, certs);
322                 if (perm != null) {
323                     if (pc == null) {
324                         pc = p.newPermissionCollection();
325                         if (pc == null)
326                             pc = new PermissionsHash();
327                     }
328                     pc.add(perm);
329                 }
330             }
331         }
332         return pc;
333     }
334 
335     private static final long serialVersionUID = 4858622370623524688L;
336 
337     // Need to maintain serialization interoperability with earlier releases,
338     // which had the serializable field:
339     // private Hashtable perms;
340 
341     /**
342      * @serialField perms java.util.Hashtable
343      *     A table of the Permission classes and PermissionCollections.
344      * @serialField allPermission java.security.PermissionCollection
345      */
346     private static final ObjectStreamField[] serialPersistentFields = {
347         new ObjectStreamField("perms", Hashtable.class),
348         new ObjectStreamField("allPermission", PermissionCollection.class),
349     };
350 
351     /**
352      * @serialData Default fields.
353      */
354     /*
355      * Writes the contents of the permsMap field out as a Hashtable for
356      * serialization compatibility with earlier releases. allPermission
357      * unchanged.
358      */
359     private void writeObject(ObjectOutputStream out) throws IOException {
360         // Don't call out.defaultWriteObject()
361 
362         // Copy perms into a Hashtable
363         Hashtable<Class<?>, PermissionCollection> perms =
364             new Hashtable<>(permsMap.size()*2); // no sync; estimate
365         synchronized (this) {
366             perms.putAll(permsMap);
367         }
368 
369         // Write out serializable fields
370         ObjectOutputStream.PutField pfields = out.putFields();
371 
372         pfields.put("allPermission", allPermission); // no sync; staleness OK
373         pfields.put("perms", perms);
374         out.writeFields();
375     }
376 
377     /*
378      * Reads in a Hashtable of Class/PermissionCollections and saves them in the
379      * permsMap field. Reads in allPermission.
380      */
381     private void readObject(ObjectInputStream in) throws IOException,
382     ClassNotFoundException {
383         // Don't call defaultReadObject()
384 
385         // Read in serialized fields
386         ObjectInputStream.GetField gfields = in.readFields();
387 
388         // Get allPermission
389         allPermission = (PermissionCollection) gfields.get("allPermission", null);
390 
391         // Get permissions
392         // writeObject writes a Hashtable<Class<?>, PermissionCollection> for
393         // the perms key, so this cast is safe, unless the data is corrupt.
394         @SuppressWarnings("unchecked")
395         Hashtable<Class<?>, PermissionCollection> perms =
396             (Hashtable<Class<?>, PermissionCollection>)gfields.get("perms", null);
397         permsMap = new HashMap<Class<?>, PermissionCollection>(perms.size()*2);
398         permsMap.putAll(perms);
399 
400         // Set hasUnresolved
401         UnresolvedPermissionCollection uc =
402         (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class);
403         hasUnresolved = (uc != null && uc.elements().hasMoreElements());
404     }
405 }
406 
407 final class PermissionsEnumerator implements Enumeration<Permission> {
408 
409     // all the perms
410     private Iterator<PermissionCollection> perms;
411     // the current set
412     private Enumeration<Permission> permset;
413 
414     PermissionsEnumerator(Iterator<PermissionCollection> e) {
415         perms = e;
416         permset = getNextEnumWithMore();
417     }
418 
419     // No need to synchronize; caller should sync on object as required
420     public boolean hasMoreElements() {
421         // if we enter with permissionimpl null, we know
422         // there are no more left.
423 
424         if (permset == null)
425             return  false;
426 
427         // try to see if there are any left in the current one
428 
429         if (permset.hasMoreElements())
430             return true;
431 
432         // get the next one that has something in it...
433         permset = getNextEnumWithMore();
434 
435         // if it is null, we are done!
436         return (permset != null);
437     }
438 
439     // No need to synchronize; caller should sync on object as required
440     public Permission nextElement() {
441 
442         // hasMoreElements will update permset to the next permset
443         // with something in it...
444 
445         if (hasMoreElements()) {
446             return permset.nextElement();
447         } else {
448             throw new NoSuchElementException("PermissionsEnumerator");
449         }
450 
451     }
452 
453     private Enumeration<Permission> getNextEnumWithMore() {
454         while (perms.hasNext()) {
455             PermissionCollection pc = perms.next();
456             Enumeration<Permission> next =pc.elements();
457             if (next.hasMoreElements())
458                 return next;
459         }
460         return null;
461 
462     }
463 }
464 
465 /**
466  * A PermissionsHash stores a homogeneous set of permissions in a hashtable.
467  *
468  * @see Permission
469  * @see Permissions
470  *
471  *
472  * @author Roland Schemers
473  *
474  * @serial include
475  */
476 
477 final class PermissionsHash extends PermissionCollection
478 implements Serializable
479 {
480     /**
481      * Key and value are (same) permissions objects.
482      * Not serialized; see serialization section at end of class.
483      */
484     private transient Map<Permission, Permission> permsMap;
485 
486     /**
487      * Create an empty PermissionsHash object.
488      */
489 
490     PermissionsHash() {
491         permsMap = new HashMap<Permission, Permission>(11);
492     }
493 
494     /**
495      * Adds a permission to the PermissionsHash.
496      *
497      * @param permission the Permission object to add.
498      */
499 
500     public void add(Permission permission) {
501         synchronized (this) {
502             permsMap.put(permission, permission);
503         }
504     }
505 
506     /**
507      * Check and see if this set of permissions implies the permissions
508      * expressed in "permission".
509      *
510      * @param permission the Permission object to compare
511      *
512      * @return true if "permission" is a proper subset of a permission in
513      * the set, false if not.
514      */
515 
516     public boolean implies(Permission permission) {
517         // attempt a fast lookup and implies. If that fails
518         // then enumerate through all the permissions.
519         synchronized (this) {
520             Permission p = permsMap.get(permission);
521 
522             // If permission is found, then p.equals(permission)
523             if (p == null) {
524                 for (Permission p_ : permsMap.values()) {
525                     if (p_.implies(permission))
526                         return true;
527                 }
528                 return false;
529             } else {
530                 return true;
531             }
532         }
533     }
534 
535     /**
536      * Returns an enumeration of all the Permission objects in the container.
537      *
538      * @return an enumeration of all the Permissions.
539      */
540 
541     public Enumeration<Permission> elements() {
542         // Convert Iterator of Map values into an Enumeration
543         synchronized (this) {
544             return Collections.enumeration(permsMap.values());
545         }
546     }
547 
548     private static final long serialVersionUID = -8491988220802933440L;
549     // Need to maintain serialization interoperability with earlier releases,
550     // which had the serializable field:
551     // private Hashtable perms;
552     /**
553      * @serialField perms java.util.Hashtable
554      *     A table of the Permissions (both key and value are same).
555      */
556     private static final ObjectStreamField[] serialPersistentFields = {
557         new ObjectStreamField("perms", Hashtable.class),
558     };
559 
560     /**
561      * @serialData Default fields.
562      */
563     /*
564      * Writes the contents of the permsMap field out as a Hashtable for
565      * serialization compatibility with earlier releases.
566      */
567     private void writeObject(ObjectOutputStream out) throws IOException {
568         // Don't call out.defaultWriteObject()
569 
570         // Copy perms into a Hashtable
571         Hashtable<Permission, Permission> perms =
572                 new Hashtable<>(permsMap.size()*2);
573         synchronized (this) {
574             perms.putAll(permsMap);
575         }
576 
577         // Write out serializable fields
578         ObjectOutputStream.PutField pfields = out.putFields();
579         pfields.put("perms", perms);
580         out.writeFields();
581     }
582 
583     /*
584      * Reads in a Hashtable of Permission/Permission and saves them in the
585      * permsMap field.
586      */
587     private void readObject(ObjectInputStream in) throws IOException,
588     ClassNotFoundException {
589         // Don't call defaultReadObject()
590 
591         // Read in serialized fields
592         ObjectInputStream.GetField gfields = in.readFields();
593 
594         // Get permissions
595         // writeObject writes a Hashtable<Class<?>, PermissionCollection> for
596         // the perms key, so this cast is safe, unless the data is corrupt.
597         @SuppressWarnings("unchecked")
598         Hashtable<Permission, Permission> perms =
599                 (Hashtable<Permission, Permission>)gfields.get("perms", null);
600         permsMap = new HashMap<Permission, Permission>(perms.size()*2);
601         permsMap.putAll(perms);
602     }
603 }