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