001 /*
002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.security;
027
028 import java.util.Enumeration;
029 import java.util.Hashtable;
030 import java.util.NoSuchElementException;
031 import java.util.Map;
032 import java.util.HashMap;
033 import java.util.List;
034 import java.util.ArrayList;
035 import java.util.Iterator;
036 import java.util.Collections;
037 import java.io.Serializable;
038 import java.io.ObjectStreamField;
039 import java.io.ObjectOutputStream;
040 import java.io.ObjectInputStream;
041 import java.io.IOException;
042
043 /**
044 * This class represents a heterogeneous collection of Permissions. That is,
045 * it contains different types of Permission objects, organized into
046 * PermissionCollections. For example, if any
047 * <code>java.io.FilePermission</code> objects are added to an instance of
048 * this class, they are all stored in a single
049 * PermissionCollection. It is the PermissionCollection returned by a call to
050 * the <code>newPermissionCollection</code> method in the FilePermission class.
051 * Similarly, any <code>java.lang.RuntimePermission</code> objects are
052 * stored in the PermissionCollection returned by a call to the
053 * <code>newPermissionCollection</code> method in the
054 * RuntimePermission class. Thus, this class represents a collection of
055 * PermissionCollections.
056 *
057 * <p>When the <code>add</code> method is called to add a Permission, the
058 * Permission is stored in the appropriate PermissionCollection. If no such
059 * collection exists yet, the Permission object's class is determined and the
060 * <code>newPermissionCollection</code> method is called on that class to create
061 * the PermissionCollection and add it to the Permissions object. If
062 * <code>newPermissionCollection</code> returns null, then a default
063 * PermissionCollection that uses a hashtable will be created and used. Each
064 * hashtable entry stores a Permission object as both the key and the value.
065 *
066 * <p> Enumerations returned via the <code>elements</code> method are
067 * not <em>fail-fast</em>. Modifications to a collection should not be
068 * performed while enumerating over that collection.
069 *
070 * @see Permission
071 * @see PermissionCollection
072 * @see AllPermission
073 *
074 * @version 1.68, 07/05/05
075 *
076 * @author Marianne Mueller
077 * @author Roland Schemers
078 *
079 * @serial exclude
080 */
081
082 public final class Permissions extends PermissionCollection implements
083 Serializable {
084 /**
085 * Key is permissions Class, value is PermissionCollection for that class.
086 * Not serialized; see serialization section at end of class.
087 */
088 private transient Map<Class<?>, PermissionCollection> permsMap;
089
090 // optimization. keep track of whether unresolved permissions need to be
091 // checked
092 private transient boolean hasUnresolved = false;
093
094 // optimization. keep track of the AllPermission collection
095 // - package private for ProtectionDomain optimization
096 PermissionCollection allPermission;
097
098 /**
099 * 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</code> method
159 * is called with a permission specifying both "read" and "write" access
160 * to files in the "/tmp/scratch/foo" directory, <code>true</code> 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(
180 permission, 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()
204 .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 // Called from within synchronized method so permsMap doesn't need lock
281
282 UnresolvedPermissionCollection uc = (UnresolvedPermissionCollection) permsMap
283 .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 = uc
290 .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",
349 PermissionCollection.class), };
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 = new Hashtable<Class<?>, PermissionCollection>(
364 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(
390 "allPermission", null);
391
392 // Get permissions
393 Hashtable<Class<?>, PermissionCollection> perms = (Hashtable<Class<?>, PermissionCollection>) gfields
394 .get("perms", null);
395 permsMap = new HashMap<Class<?>, PermissionCollection>(perms
396 .size() * 2);
397 permsMap.putAll(perms);
398
399 // Set hasUnresolved
400 UnresolvedPermissionCollection uc = (UnresolvedPermissionCollection) permsMap
401 .get(UnresolvedPermission.class);
402 hasUnresolved = (uc != null && uc.elements().hasMoreElements());
403 }
404 }
405
406 final class PermissionsEnumerator implements Enumeration<Permission> {
407
408 // all the perms
409 private Iterator<PermissionCollection> perms;
410 // the current set
411 private Enumeration<Permission> permset;
412
413 PermissionsEnumerator(Iterator<PermissionCollection> e) {
414 perms = e;
415 permset = getNextEnumWithMore();
416 }
417
418 // No need to synchronize; caller should sync on object as required
419 public boolean hasMoreElements() {
420 // if we enter with permissionimpl null, we know
421 // there are no more left.
422
423 if (permset == null)
424 return false;
425
426 // try to see if there are any left in the current one
427
428 if (permset.hasMoreElements())
429 return true;
430
431 // get the next one that has something in it...
432 permset = getNextEnumWithMore();
433
434 // if it is null, we are done!
435 return (permset != null);
436 }
437
438 // No need to synchronize; caller should sync on object as required
439 public Permission nextElement() {
440
441 // hasMoreElements will update permset to the next permset
442 // with something in it...
443
444 if (hasMoreElements()) {
445 return permset.nextElement();
446 } else {
447 throw new NoSuchElementException("PermissionsEnumerator");
448 }
449
450 }
451
452 private Enumeration<Permission> getNextEnumWithMore() {
453 while (perms.hasNext()) {
454 PermissionCollection pc = (PermissionCollection) perms
455 .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 * @version 1.68, 05/05/07
472 *
473 * @author Roland Schemers
474 *
475 * @serial include
476 */
477
478 final class PermissionsHash extends PermissionCollection implements
479 Serializable {
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 = { new ObjectStreamField(
557 "perms", Hashtable.class), };
558
559 /**
560 * @serialData Default fields.
561 */
562 /*
563 * Writes the contents of the permsMap field out as a Hashtable for
564 * serialization compatibility with earlier releases.
565 */
566 private void writeObject(ObjectOutputStream out) throws IOException {
567 // Don't call out.defaultWriteObject()
568
569 // Copy perms into a Hashtable
570 Hashtable<Permission, Permission> perms = new Hashtable<Permission, Permission>(
571 permsMap.size() * 2);
572 synchronized (this ) {
573 perms.putAll(permsMap);
574 }
575
576 // Write out serializable fields
577 ObjectOutputStream.PutField pfields = out.putFields();
578 pfields.put("perms", perms);
579 out.writeFields();
580 }
581
582 /*
583 * Reads in a Hashtable of Permission/Permission and saves them in the
584 * permsMap field.
585 */
586 private void readObject(ObjectInputStream in) throws IOException,
587 ClassNotFoundException {
588 // Don't call defaultReadObject()
589
590 // Read in serialized fields
591 ObjectInputStream.GetField gfields = in.readFields();
592
593 // Get permissions
594 Hashtable<Permission, Permission> perms = (Hashtable<Permission, Permission>) gfields
595 .get("perms", null);
596 permsMap = new HashMap<Permission, Permission>(perms.size() * 2);
597 permsMap.putAll(perms);
598 }
599 }
|