001 /*
002 * Copyright 1997-2007 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.security.*;
029 import java.util.Enumeration;
030 import java.util.Iterator;
031 import java.util.Map;
032 import java.util.HashMap;
033 import java.util.Hashtable;
034 import java.util.Collections;
035 import java.util.StringTokenizer;
036 import java.io.ObjectStreamField;
037 import java.io.ObjectOutputStream;
038 import java.io.ObjectInputStream;
039 import java.io.IOException;
040
041 /**
042 * The BasicPermission class extends the Permission class, and
043 * can be used as the base class for permissions that want to
044 * follow the same naming convention as BasicPermission.
045 * <P>
046 * The name for a BasicPermission is the name of the given permission
047 * (for example, "exit",
048 * "setFactory", "print.queueJob", etc). The naming
049 * convention follows the hierarchical property naming convention.
050 * An asterisk may appear by itself, or if immediately preceded by a "."
051 * may appear at the end of the name, to signify a wildcard match.
052 * For example, "*" and "java.*" are valid, while "*java", "a*b",
053 * and "java*" are not valid.
054 * <P>
055 * The action string (inherited from Permission) is unused.
056 * Thus, BasicPermission is commonly used as the base class for
057 * "named" permissions
058 * (ones that contain a name but no actions list; you either have the
059 * named permission or you don't.)
060 * Subclasses may implement actions on top of BasicPermission,
061 * if desired.
062 * <p>
063 * <P>
064 * @see java.security.Permission
065 * @see java.security.Permissions
066 * @see java.security.PermissionCollection
067 * @see java.lang.RuntimePermission
068 * @see java.security.SecurityPermission
069 * @see java.util.PropertyPermission
070 * @see java.awt.AWTPermission
071 * @see java.net.NetPermission
072 * @see java.lang.SecurityManager
073 *
074 * @version 1.54 07/07/18
075 *
076 * @author Marianne Mueller
077 * @author Roland Schemers
078 */
079
080 public abstract class BasicPermission extends Permission implements
081 java.io.Serializable {
082
083 private static final long serialVersionUID = 6279438298436773498L;
084
085 // does this permission have a wildcard at the end?
086 private transient boolean wildcard;
087
088 // the name without the wildcard on the end
089 private transient String path;
090
091 // is this permission the old-style exitVM permission (pre JDK 1.6)?
092 private transient boolean exitVM;
093
094 /**
095 * initialize a BasicPermission object. Common to all constructors.
096 *
097 */
098
099 private void init(String name) {
100 if (name == null)
101 throw new NullPointerException("name can't be null");
102
103 int len = name.length();
104
105 if (len == 0) {
106 throw new IllegalArgumentException("name can't be empty");
107 }
108
109 char last = name.charAt(len - 1);
110
111 // Is wildcard or ends with ".*"?
112 if (last == '*' && (len == 1 || name.charAt(len - 2) == '.')) {
113 wildcard = true;
114 if (len == 1) {
115 path = "";
116 } else {
117 path = name.substring(0, len - 1);
118 }
119 } else {
120 if (name.equals("exitVM")) {
121 wildcard = true;
122 path = "exitVM.";
123 exitVM = true;
124 } else {
125 path = name;
126 }
127 }
128 }
129
130 /**
131 * Creates a new BasicPermission with the specified name.
132 * Name is the symbolic name of the permission, such as
133 * "setFactory",
134 * "print.queueJob", or "topLevelWindow", etc.
135 *
136 * @param name the name of the BasicPermission.
137 *
138 * @throws NullPointerException if <code>name</code> is <code>null</code>.
139 * @throws IllegalArgumentException if <code>name</code> is empty.
140 */
141
142 public BasicPermission(String name) {
143 super (name);
144 init(name);
145 }
146
147 /**
148 * Creates a new BasicPermission object with the specified name.
149 * The name is the symbolic name of the BasicPermission, and the
150 * actions String is currently unused.
151 *
152 * @param name the name of the BasicPermission.
153 * @param actions ignored.
154 *
155 * @throws NullPointerException if <code>name</code> is <code>null</code>.
156 * @throws IllegalArgumentException if <code>name</code> is empty.
157 */
158 public BasicPermission(String name, String actions) {
159 super (name);
160 init(name);
161 }
162
163 /**
164 * Checks if the specified permission is "implied" by
165 * this object.
166 * <P>
167 * More specifically, this method returns true if:<p>
168 * <ul>
169 * <li> <i>p</i>'s class is the same as this object's class, and<p>
170 * <li> <i>p</i>'s name equals or (in the case of wildcards)
171 * is implied by this object's
172 * name. For example, "a.b.*" implies "a.b.c".
173 * </ul>
174 *
175 * @param p the permission to check against.
176 *
177 * @return true if the passed permission is equal to or
178 * implied by this permission, false otherwise.
179 */
180 public boolean implies(Permission p) {
181 if ((p == null) || (p.getClass() != getClass()))
182 return false;
183
184 BasicPermission that = (BasicPermission) p;
185
186 if (this .wildcard) {
187 if (that.wildcard) {
188 // one wildcard can imply another
189 return that.path.startsWith(path);
190 } else {
191 // make sure ap.path is longer so a.b.* doesn't imply a.b
192 return (that.path.length() > this .path.length())
193 && that.path.startsWith(this .path);
194 }
195 } else {
196 if (that.wildcard) {
197 // a non-wildcard can't imply a wildcard
198 return false;
199 } else {
200 return this .path.equals(that.path);
201 }
202 }
203 }
204
205 /**
206 * Checks two BasicPermission objects for equality.
207 * Checks that <i>obj</i>'s class is the same as this object's class
208 * and has the same name as this object.
209 * <P>
210 * @param obj the object we are testing for equality with this object.
211 * @return true if <i>obj</i> is a BasicPermission, and has the same name
212 * as this BasicPermission object, false otherwise.
213 */
214 public boolean equals(Object obj) {
215 if (obj == this )
216 return true;
217
218 if ((obj == null) || (obj.getClass() != getClass()))
219 return false;
220
221 BasicPermission bp = (BasicPermission) obj;
222
223 return getName().equals(bp.getName());
224 }
225
226 /**
227 * Returns the hash code value for this object.
228 * The hash code used is the hash code of the name, that is,
229 * <code>getName().hashCode()</code>, where <code>getName</code> is
230 * from the Permission superclass.
231 *
232 * @return a hash code value for this object.
233 */
234
235 public int hashCode() {
236 return this .getName().hashCode();
237 }
238
239 /**
240 * Returns the canonical string representation of the actions,
241 * which currently is the empty string "", since there are no actions for
242 * a BasicPermission.
243 *
244 * @return the empty string "".
245 */
246 public String getActions() {
247 return "";
248 }
249
250 /**
251 * Returns a new PermissionCollection object for storing BasicPermission
252 * objects.
253 *
254 * <p>BasicPermission objects must be stored in a manner that allows them
255 * to be inserted in any order, but that also enables the
256 * PermissionCollection <code>implies</code> method
257 * to be implemented in an efficient (and consistent) manner.
258 *
259 * @return a new PermissionCollection object suitable for
260 * storing BasicPermissions.
261 */
262
263 public PermissionCollection newPermissionCollection() {
264 return new BasicPermissionCollection(this .getClass());
265 }
266
267 /**
268 * readObject is called to restore the state of the BasicPermission from
269 * a stream.
270 */
271 private void readObject(ObjectInputStream s) throws IOException,
272 ClassNotFoundException {
273 s.defaultReadObject();
274 // init is called to initialize the rest of the values.
275 init(getName());
276 }
277
278 /**
279 * Returns the canonical name of this BasicPermission.
280 * All internal invocations of getName should invoke this method, so
281 * that the pre-JDK 1.6 "exitVM" and current "exitVM.*" permission are
282 * equivalent in equals/hashCode methods.
283 *
284 * @return the canonical name of this BasicPermission.
285 */
286 final String getCanonicalName() {
287 return exitVM ? "exitVM.*" : getName();
288 }
289 }
290
291 /**
292 * A BasicPermissionCollection stores a collection
293 * of BasicPermission permissions. BasicPermission objects
294 * must be stored in a manner that allows them to be inserted in any
295 * order, but enable the implies function to evaluate the implies
296 * method in an efficient (and consistent) manner.
297 *
298 * A BasicPermissionCollection handles comparing a permission like "a.b.c.d.e"
299 * with a Permission such as "a.b.*", or "*".
300 *
301 * @see java.security.Permission
302 * @see java.security.Permissions
303 * @see java.security.PermissionsImpl
304 *
305 * @version 1.54 07/18/07
306 *
307 * @author Roland Schemers
308 *
309 * @serial include
310 */
311
312 final class BasicPermissionCollection extends PermissionCollection
313 implements java.io.Serializable {
314
315 private static final long serialVersionUID = 739301742472979399L;
316
317 /**
318 * Key is name, value is permission. All permission objects in
319 * collection must be of the same type.
320 * Not serialized; see serialization section at end of class.
321 */
322 private transient Map<String, Permission> perms;
323
324 /**
325 * This is set to <code>true</code> if this BasicPermissionCollection
326 * contains a BasicPermission with '*' as its permission name.
327 *
328 * @see #serialPersistentFields
329 */
330 private boolean all_allowed;
331
332 /**
333 * The class to which all BasicPermissions in this
334 * BasicPermissionCollection belongs.
335 *
336 * @see #serialPersistentFields
337 */
338 private Class permClass;
339
340 /**
341 * Create an empty BasicPermissionCollection object.
342 *
343 */
344
345 public BasicPermissionCollection(Class clazz) {
346 perms = new HashMap<String, Permission>(11);
347 all_allowed = false;
348 permClass = clazz;
349 }
350
351 /**
352 * Adds a permission to the BasicPermissions. The key for the hash is
353 * permission.path.
354 *
355 * @param permission the Permission object to add.
356 *
357 * @exception IllegalArgumentException - if the permission is not a
358 * BasicPermission, or if
359 * the permission is not of the
360 * same Class as the other
361 * permissions in this collection.
362 *
363 * @exception SecurityException - if this BasicPermissionCollection object
364 * has been marked readonly
365 */
366
367 public void add(Permission permission) {
368 if (!(permission instanceof BasicPermission))
369 throw new IllegalArgumentException("invalid permission: "
370 + permission);
371 if (isReadOnly())
372 throw new SecurityException(
373 "attempt to add a Permission to a readonly PermissionCollection");
374
375 BasicPermission bp = (BasicPermission) permission;
376
377 // make sure we only add new BasicPermissions of the same class
378 // Also check null for compatibility with deserialized form from
379 // previous versions.
380 if (permClass == null) {
381 // adding first permission
382 permClass = bp.getClass();
383 } else {
384 if (bp.getClass() != permClass)
385 throw new IllegalArgumentException(
386 "invalid permission: " + permission);
387 }
388
389 synchronized (this ) {
390 perms.put(bp.getCanonicalName(), permission);
391 }
392
393 // No sync on all_allowed; staleness OK
394 if (!all_allowed) {
395 if (bp.getCanonicalName().equals("*"))
396 all_allowed = true;
397 }
398 }
399
400 /**
401 * Check and see if this set of permissions implies the permissions
402 * expressed in "permission".
403 *
404 * @param p the Permission object to compare
405 *
406 * @return true if "permission" is a proper subset of a permission in
407 * the set, false if not.
408 */
409
410 public boolean implies(Permission permission) {
411 if (!(permission instanceof BasicPermission))
412 return false;
413
414 BasicPermission bp = (BasicPermission) permission;
415
416 // random subclasses of BasicPermission do not imply each other
417 if (bp.getClass() != permClass)
418 return false;
419
420 // short circuit if the "*" Permission was added
421 if (all_allowed)
422 return true;
423
424 // strategy:
425 // Check for full match first. Then work our way up the
426 // path looking for matches on a.b..*
427
428 String path = bp.getCanonicalName();
429 //System.out.println("check "+path);
430
431 Permission x;
432
433 synchronized (this ) {
434 x = perms.get(path);
435 }
436
437 if (x != null) {
438 // we have a direct hit!
439 return x.implies(permission);
440 }
441
442 // work our way up the tree...
443 int last, offset;
444
445 offset = path.length() - 1;
446
447 while ((last = path.lastIndexOf(".", offset)) != -1) {
448
449 path = path.substring(0, last + 1) + "*";
450 //System.out.println("check "+path);
451
452 synchronized (this ) {
453 x = perms.get(path);
454 }
455
456 if (x != null) {
457 return x.implies(permission);
458 }
459 offset = last - 1;
460 }
461
462 // we don't have to check for "*" as it was already checked
463 // at the top (all_allowed), so we just return false
464 return false;
465 }
466
467 /**
468 * Returns an enumeration of all the BasicPermission objects in the
469 * container.
470 *
471 * @return an enumeration of all the BasicPermission objects.
472 */
473
474 public Enumeration<Permission> elements() {
475 // Convert Iterator of Map values into an Enumeration
476 synchronized (this ) {
477 return Collections.enumeration(perms.values());
478 }
479 }
480
481 // Need to maintain serialization interoperability with earlier releases,
482 // which had the serializable field:
483 //
484 // @serial the Hashtable is indexed by the BasicPermission name
485 //
486 // private Hashtable permissions;
487 /**
488 * @serialField permissions java.util.Hashtable
489 * The BasicPermissions in this BasicPermissionCollection.
490 * All BasicPermissions in the collection must belong to the same class.
491 * The Hashtable is indexed by the BasicPermission name; the value
492 * of the Hashtable entry is the permission.
493 * @serialField all_allowed boolean
494 * This is set to <code>true</code> if this BasicPermissionCollection
495 * contains a BasicPermission with '*' as its permission name.
496 * @serialField permClass java.lang.Class
497 * The class to which all BasicPermissions in this
498 * BasicPermissionCollection belongs.
499 */
500 private static final ObjectStreamField[] serialPersistentFields = {
501 new ObjectStreamField("permissions", Hashtable.class),
502 new ObjectStreamField("all_allowed", Boolean.TYPE),
503 new ObjectStreamField("permClass", Class.class), };
504
505 /**
506 * @serialData Default fields.
507 */
508 /*
509 * Writes the contents of the perms field out as a Hashtable for
510 * serialization compatibility with earlier releases. all_allowed
511 * and permClass unchanged.
512 */
513 private void writeObject(ObjectOutputStream out) throws IOException {
514 // Don't call out.defaultWriteObject()
515
516 // Copy perms into a Hashtable
517 Hashtable<String, Permission> permissions = new Hashtable<String, Permission>(
518 perms.size() * 2);
519
520 synchronized (this ) {
521 permissions.putAll(perms);
522 }
523
524 // Write out serializable fields
525 ObjectOutputStream.PutField pfields = out.putFields();
526 pfields.put("all_allowed", all_allowed);
527 pfields.put("permissions", permissions);
528 pfields.put("permClass", permClass);
529 out.writeFields();
530 }
531
532 /**
533 * readObject is called to restore the state of the
534 * BasicPermissionCollection from a stream.
535 */
536 private void readObject(java.io.ObjectInputStream in)
537 throws IOException, ClassNotFoundException {
538 // Don't call defaultReadObject()
539
540 // Read in serialized fields
541 ObjectInputStream.GetField gfields = in.readFields();
542
543 // Get permissions
544 Hashtable<String, Permission> permissions = (Hashtable<String, Permission>) gfields
545 .get("permissions", null);
546 perms = new HashMap<String, Permission>(permissions.size() * 2);
547 perms.putAll(permissions);
548
549 // Get all_allowed
550 all_allowed = gfields.get("all_allowed", false);
551
552 // Get permClass
553 permClass = (Class) gfields.get("permClass", null);
554
555 if (permClass == null) {
556 // set permClass
557 Enumeration<Permission> e = permissions.elements();
558 if (e.hasMoreElements()) {
559 Permission p = e.nextElement();
560 permClass = p.getClass();
561 }
562 }
563 }
564 }
|