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
26package java.security;
27
28import java.util.Enumeration;
29import java.util.Hashtable;
30import java.util.NoSuchElementException;
31import java.util.Map;
32import java.util.HashMap;
33import java.util.List;
34import java.util.Iterator;
35import java.util.Collections;
36import java.util.concurrent.ConcurrentHashMap;
37import java.io.Serializable;
38import java.io.ObjectStreamField;
39import java.io.ObjectOutputStream;
40import java.io.ObjectInputStream;
41import 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
83public final class Permissions extends PermissionCollection
84implements 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
402final 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
472final class PermissionsHash extends PermissionCollection
473implements 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