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