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.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.util.concurrent.ConcurrentHashMap;
37 import java.io.Serializable;
38 import java.io.ObjectStreamField;
39 import java.io.ObjectOutputStream;
40 import java.io.ObjectInputStream;
41 import java.io.IOException;
42
43
44 /**
45  * This class represents a heterogeneous collection of Permissions. That is,
46  * it contains different types of Permission objects, organized into
47  * PermissionCollections. For example, if any
48  * {@code java.io.FilePermission} objects are added to an instance of
49  * this class, they are all stored in a single
50  * PermissionCollection. It is the PermissionCollection returned by a call to
51  * the {@code newPermissionCollection} method in the FilePermission class.
52  * Similarly, any {@code java.lang.RuntimePermission} objects are
53  * stored in the PermissionCollection returned by a call to the
54  * {@code newPermissionCollection} method in the
55  * RuntimePermission class. Thus, this class represents a collection of
56  * PermissionCollections.
57  *
58  * <p>When the {@code add} method is called to add a Permission, the
59  * Permission is stored in the appropriate PermissionCollection. If no such
60  * collection exists yet, the Permission object's class is determined and the
61  * {@code newPermissionCollection} method is called on that class to create
62  * the PermissionCollection and add it to the Permissions object. If
63  * {@code newPermissionCollection} returns null, then a default
64  * PermissionCollection that uses a hashtable will be created and used. Each
65  * hashtable entry stores a Permission object as both the key and the value.
66  *
67  * <p> Enumerations returned via the {@code elements} method are
68  * not <em>fail-fast</em>.  Modifications to a collection should not be
69  * performed while enumerating over that collection.
70  *
71  * @see Permission
72  * @see PermissionCollection
73  * @see AllPermission
74  *
75  *
76  * @author Marianne Mueller
77  * @author Roland Schemers
78  * @since 1.2
79  *
80  * @serial exclude
81  */

82
83 public final class Permissions extends PermissionCollection
84 implements Serializable
85 {
86     /**
87      * Key is permissions Class, value is PermissionCollection for that class.
88      * Not serialized; see serialization section at end of class.
89      */

90     private transient ConcurrentHashMap<Class<?>, PermissionCollection> permsMap;
91
92     // optimization. keep track of whether unresolved permissions need to be
93     // checked
94     private transient boolean hasUnresolved = false;
95
96     // optimization. keep track of the AllPermission collection
97     // - package private for ProtectionDomain optimization
98     PermissionCollection allPermission;
99
100     /**
101      * Creates a new Permissions object containing no PermissionCollections.
102      */

103     public Permissions() {
104         permsMap = new ConcurrentHashMap<>(11);
105         allPermission = null;
106     }
107
108     /**
109      * Adds a permission object to the PermissionCollection for the class the
110      * permission belongs to. For example, if <i>permission</i> is a
111      * FilePermission, it is added to the FilePermissionCollection stored
112      * in this Permissions object.
113      *
114      * This method creates
115      * a new PermissionCollection object (and adds the permission to it)
116      * if an appropriate collection does not yet exist.
117      *
118      * @param permission the Permission object to add.
119      *
120      * @exception SecurityException if this Permissions object is
121      * marked as readonly.
122      *
123      * @see PermissionCollection#isReadOnly()
124      */

125     @Override
126     public void add(Permission permission) {
127         if (isReadOnly())
128             throw new SecurityException(
129               "attempt to add a Permission to a readonly Permissions object");
130
131         PermissionCollection pc = getPermissionCollection(permission, true);
132         pc.add(permission);
133
134         // No sync; staleness -> optimizations delayed, which is OK
135         if (permission instanceof AllPermission) {
136             allPermission = pc;
137         }
138         if (permission instanceof UnresolvedPermission) {
139             hasUnresolved = true;
140         }
141     }
142
143     /**
144      * Checks to see if this object's PermissionCollection for permissions of
145      * the specified permission's class implies the permissions
146      * expressed in the <i>permission</i> object. Returns true if the
147      * combination of permissions in the appropriate PermissionCollection
148      * (e.g., a FilePermissionCollection for a FilePermission) together
149      * imply the specified permission.
150      *
151      * <p>For example, suppose there is a FilePermissionCollection in this
152      * Permissions object, and it contains one FilePermission that specifies
153      * "read" access for  all files in all subdirectories of the "/tmp"
154      * directory, and another FilePermission that specifies "write" access
155      * for all files in the "/tmp/scratch/foo" directory.
156      * Then if the {@code implies} method
157      * is called with a permission specifying both "read" and "write" access
158      * to files in the "/tmp/scratch/foo" directory, {@code true} is
159      * returned.
160      *
161      * <p>Additionally, if this PermissionCollection contains the
162      * AllPermission, this method will always return true.
163      *
164      * @param permission the Permission object to check.
165      *
166      * @return true if "permission" is implied by the permissions in the
167      * PermissionCollection it
168      * belongs to, false if not.
169      */

170     @Override
171     public boolean implies(Permission permission) {
172         // No sync; staleness -> skip optimization, which is OK
173         if (allPermission != null) {
174             return true// AllPermission has already been added
175         } else {
176             PermissionCollection pc = getPermissionCollection(permission,
177                 false);
178             if (pc != null) {
179                 return pc.implies(permission);
180             } else {
181                 // none found
182                 return false;
183             }
184         }
185     }
186
187     /**
188      * Returns an enumeration of all the Permission objects in all the
189      * PermissionCollections in this Permissions object.
190      *
191      * @return an enumeration of all the Permissions.
192      */

193     @Override
194     public Enumeration<Permission> elements() {
195         // go through each Permissions in the hash table
196         // and call their elements() function.
197
198         return new PermissionsEnumerator(permsMap.values().iterator());
199     }
200
201     /**
202      * Gets the PermissionCollection in this Permissions object for
203      * permissions whose type is the same as that of <i>p</i>.
204      * For example, if <i>p</i> is a FilePermission,
205      * the FilePermissionCollection
206      * stored in this Permissions object will be returned.
207      *
208      * If createEmpty is true,
209      * this method creates a new PermissionCollection object for the specified
210      * type of permission objects if one does not yet exist.
211      * To do so, it first calls the {@code newPermissionCollection} method
212      * on <i>p</i>.  Subclasses of class Permission
213      * override that method if they need to store their permissions in a
214      * particular PermissionCollection object in order to provide the
215      * correct semantics when the {@code PermissionCollection.implies}
216      * method is called.
217      * If the call returns a PermissionCollection, that collection is stored
218      * in this Permissions object. If the call returns null and createEmpty
219      * is true, then
220      * this method instantiates and stores a default PermissionCollection
221      * that uses a hashtable to store its permission objects.
222      *
223      * createEmpty is ignored when creating empty PermissionCollection
224      * for unresolved permissions because of the overhead of determining the
225      * PermissionCollection to use.
226      *
227      * createEmpty should be set to false when this method is invoked from
228      * implies() because it incurs the additional overhead of creating and
229      * adding an empty PermissionCollection that will just return false.
230      * It should be set to true when invoked from add().
231      */

232     private PermissionCollection getPermissionCollection(Permission p,
233                                                          boolean createEmpty) {
234         Class<?> c = p.getClass();
235
236         if (!hasUnresolved && !createEmpty) {
237             return permsMap.get(c);
238         }
239
240         // Create and add permission collection to map if it is absent.
241         // NOTE: cannot use lambda for mappingFunction parameter until
242         // JDK-8076596 is fixed.
243         return permsMap.computeIfAbsent(c,
244             new java.util.function.Function<>() {
245                 @Override
246                 public PermissionCollection apply(Class<?> k) {
247                     // Check for unresolved permissions
248                     PermissionCollection pc =
249                         (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                     return pc;
263                 }
264             }
265         );
266     }
267
268     /**
269      * Resolves any unresolved permissions of type p.
270      *
271      * @param p the type of unresolved permission to resolve
272      *
273      * @return PermissionCollection containing the unresolved permissions,
274      *  or null if there were no unresolved permissions of type p.
275      *
276      */

277     private PermissionCollection getUnresolvedPermissions(Permission p)
278     {
279         UnresolvedPermissionCollection uc =
280         (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class);
281
282         // we have no unresolved permissions if uc is null
283         if (uc == null)
284             return null;
285
286         List<UnresolvedPermission> unresolvedPerms =
287                                         uc.getUnresolvedPermissions(p);
288
289         // we have no unresolved permissions of this type if unresolvedPerms is null
290         if (unresolvedPerms == null)
291             return null;
292
293         java.security.cert.Certificate[] certs = null;
294
295         Object[] signers = p.getClass().getSigners();
296
297         int n = 0;
298         if (signers != null) {
299             for (int j=0; j < signers.length; j++) {
300                 if (signers[j] instanceof java.security.cert.Certificate) {
301                     n++;
302                 }
303             }
304             certs = new java.security.cert.Certificate[n];
305             n = 0;
306             for (int j=0; j < signers.length; j++) {
307                 if (signers[j] instanceof java.security.cert.Certificate) {
308                     certs[n++] = (java.security.cert.Certificate)signers[j];
309                 }
310             }
311         }
312
313         PermissionCollection pc = null;
314         synchronized (unresolvedPerms) {
315             int len = unresolvedPerms.size();
316             for (int i = 0; i < len; i++) {
317                 UnresolvedPermission up = unresolvedPerms.get(i);
318                 Permission perm = up.resolve(p, certs);
319                 if (perm != null) {
320                     if (pc == null) {
321                         pc = p.newPermissionCollection();
322                         if (pc == null)
323                             pc = new PermissionsHash();
324                     }
325                     pc.add(perm);
326                 }
327             }
328         }
329         return pc;
330     }
331
332     private static final long serialVersionUID = 4858622370623524688L;
333
334     // Need to maintain serialization interoperability with earlier releases,
335     // which had the serializable field:
336     // private Hashtable perms;
337
338     /**
339      * @serialField perms java.util.Hashtable
340      *     A table of the Permission classes and PermissionCollections.
341      * @serialField allPermission java.security.PermissionCollection
342      */

343     private static final ObjectStreamField[] serialPersistentFields = {
344         new ObjectStreamField("perms", Hashtable.class),
345         new ObjectStreamField("allPermission", PermissionCollection.class),
346     };
347
348     /**
349      * @serialData Default fields.
350      */

351     /*
352      * Writes the contents of the permsMap field out as a Hashtable for
353      * serialization compatibility with earlier releases. allPermission
354      * unchanged.
355      */

356     private void writeObject(ObjectOutputStream out) throws IOException {
357         // Don't call out.defaultWriteObject()
358
359         // Copy perms into a Hashtable
360         Hashtable<Class<?>, PermissionCollection> perms =
361             new Hashtable<>(permsMap.size()*2); // no sync; estimate
362         perms.putAll(permsMap);
363
364         // Write out serializable fields
365         ObjectOutputStream.PutField pfields = out.putFields();
366
367         pfields.put("allPermission", allPermission); // no sync; staleness OK
368         pfields.put("perms", perms);
369         out.writeFields();
370     }
371
372     /*
373      * Reads in a Hashtable of Class/PermissionCollections and saves them in the
374      * permsMap field. Reads in allPermission.
375      */

376     private void readObject(ObjectInputStream in) throws IOException,
377     ClassNotFoundException {
378         // Don't call defaultReadObject()
379
380         // Read in serialized fields
381         ObjectInputStream.GetField gfields = in.readFields();
382
383         // Get allPermission
384         allPermission = (PermissionCollection) gfields.get("allPermission"null);
385
386         // Get permissions
387         // writeObject writes a Hashtable<Class<?>, PermissionCollection> for
388         // the perms key, so this cast is safe, unless the data is corrupt.
389         @SuppressWarnings("unchecked")
390         Hashtable<Class<?>, PermissionCollection> perms =
391             (Hashtable<Class<?>, PermissionCollection>)gfields.get("perms"null);
392         permsMap = new ConcurrentHashMap<>(perms.size()*2);
393         permsMap.putAll(perms);
394
395         // Set hasUnresolved
396         UnresolvedPermissionCollection uc =
397         (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class);
398         hasUnresolved = (uc != null && uc.elements().hasMoreElements());
399     }
400 }
401
402 final class PermissionsEnumerator implements Enumeration<Permission> {
403
404     // all the perms
405     private Iterator<PermissionCollection> perms;
406     // the current set
407     private Enumeration<Permission> permset;
408
409     PermissionsEnumerator(Iterator<PermissionCollection> e) {
410         perms = e;
411         permset = getNextEnumWithMore();
412     }
413
414     // No need to synchronize; caller should sync on object as required
415     public boolean hasMoreElements() {
416         // if we enter with permissionimpl null, we know
417         // there are no more left.
418
419         if (permset == null)
420             return  false;
421
422         // try to see if there are any left in the current one
423
424         if (permset.hasMoreElements())
425             return true;
426
427         // get the next one that has something in it...
428         permset = getNextEnumWithMore();
429
430         // if it is null, we are done!
431         return (permset != null);
432     }
433
434     // No need to synchronize; caller should sync on object as required
435     public Permission nextElement() {
436
437         // hasMoreElements will update permset to the next permset
438         // with something in it...
439
440         if (hasMoreElements()) {
441             return permset.nextElement();
442         } else {
443             throw new NoSuchElementException("PermissionsEnumerator");
444         }
445
446     }
447
448     private Enumeration<Permission> getNextEnumWithMore() {
449         while (perms.hasNext()) {
450             PermissionCollection pc = perms.next();
451             Enumeration<Permission> next =pc.elements();
452             if (next.hasMoreElements())
453                 return next;
454         }
455         return null;
456
457     }
458 }
459
460 /**
461  * A PermissionsHash stores a homogeneous set of permissions in a hashtable.
462  *
463  * @see Permission
464  * @see Permissions
465  *
466  *
467  * @author Roland Schemers
468  *
469  * @serial include
470  */

471
472 final class PermissionsHash extends PermissionCollection
473 implements Serializable
474 {
475     /**
476      * Key and value are (same) permissions objects.
477      * Not serialized; see serialization section at end of class.
478      */

479     private transient ConcurrentHashMap<Permission, Permission> permsMap;
480
481     /**
482      * Create an empty PermissionsHash object.
483      */

484     PermissionsHash() {
485         permsMap = new ConcurrentHashMap<>(11);
486     }
487
488     /**
489      * Adds a permission to the PermissionsHash.
490      *
491      * @param permission the Permission object to add.
492      */

493     @Override
494     public void add(Permission permission) {
495         permsMap.put(permission, permission);
496     }
497
498     /**
499      * Check and see if this set of permissions implies the permissions
500      * expressed in "permission".
501      *
502      * @param permission the Permission object to compare
503      *
504      * @return true if "permission" is a proper subset of a permission in
505      * the set, false if not.
506      */

507     @Override
508     public boolean implies(Permission permission) {
509         // attempt a fast lookup and implies. If that fails
510         // then enumerate through all the permissions.
511         Permission p = permsMap.get(permission);
512
513         // If permission is found, then p.equals(permission)
514         if (p == null) {
515             for (Permission p_ : permsMap.values()) {
516                 if (p_.implies(permission))
517                     return true;
518             }
519             return false;
520         } else {
521             return true;
522         }
523     }
524
525     /**
526      * Returns an enumeration of all the Permission objects in the container.
527      *
528      * @return an enumeration of all the Permissions.
529      */

530     @Override
531     public Enumeration<Permission> elements() {
532         return permsMap.elements();
533     }
534
535     private static final long serialVersionUID = -8491988220802933440L;
536     // Need to maintain serialization interoperability with earlier releases,
537     // which had the serializable field:
538     // private Hashtable perms;
539     /**
540      * @serialField perms java.util.Hashtable
541      *     A table of the Permissions (both key and value are same).
542      */

543     private static final ObjectStreamField[] serialPersistentFields = {
544         new ObjectStreamField("perms", Hashtable.class),
545     };
546
547     /**
548      * @serialData Default fields.
549      */

550     /*
551      * Writes the contents of the permsMap field out as a Hashtable for
552      * serialization compatibility with earlier releases.
553      */

554     private void writeObject(ObjectOutputStream out) throws IOException {
555         // Don't call out.defaultWriteObject()
556
557         // Copy perms into a Hashtable
558         Hashtable<Permission, Permission> perms =
559                 new Hashtable<>(permsMap.size()*2);
560         perms.putAll(permsMap);
561
562         // Write out serializable fields
563         ObjectOutputStream.PutField pfields = out.putFields();
564         pfields.put("perms", perms);
565         out.writeFields();
566     }
567
568     /*
569      * Reads in a Hashtable of Permission/Permission and saves them in the
570      * permsMap field.
571      */

572     private void readObject(ObjectInputStream in) throws IOException,
573     ClassNotFoundException {
574         // Don't call defaultReadObject()
575
576         // Read in serialized fields
577         ObjectInputStream.GetField gfields = in.readFields();
578
579         // Get permissions
580         // writeObject writes a Hashtable<Class<?>, PermissionCollection> for
581         // the perms key, so this cast is safe, unless the data is corrupt.
582         @SuppressWarnings("unchecked")
583         Hashtable<Permission, Permission> perms =
584                 (Hashtable<Permission, Permission>)gfields.get("perms"null);
585         permsMap = new ConcurrentHashMap<>(perms.size()*2);
586         permsMap.putAll(perms);
587     }
588 }
589