001: /*
002: * @(#)PropertyPermission.java 1.32 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.util;
029:
030: import java.io.Serializable;
031: import java.io.IOException;
032: import java.security.*;
033: import java.util.Map;
034: import java.util.HashMap;
035: import java.util.Enumeration;
036: import java.util.Hashtable;
037: import java.util.Collections;
038: import java.io.ObjectStreamField;
039: import java.io.ObjectOutputStream;
040: import java.io.ObjectInputStream;
041: import java.io.IOException;
042: import sun.security.util.SecurityConstants;
043:
044: /**
045: * This class is for property permissions.
046: *
047: * <P>
048: * The name is the name of the property ("java.home",
049: * "os.name", etc). The naming
050: * convention follows the hierarchical property naming convention.
051: * Also, an asterisk
052: * may appear at the end of the name, following a ".", or by itself, to
053: * signify a wildcard match. For example: "java.*" or "*" is valid,
054: * "*java" or "a*b" is not valid.
055: * <P>
056: * <P>
057: * The actions to be granted are passed to the constructor in a string containing
058: * a list of zero or more comma-separated keywords. The possible keywords are
059: * "read" and "write". Their meaning is defined as follows:
060: * <P>
061: * <DL>
062: * <DT> read
063: * <DD> read permission. Allows <code>System.getProperty</code> to
064: * be called.
065: * <DT> write
066: * <DD> write permission. Allows <code>System.setProperty</code> to
067: * be called.
068: * </DL>
069: * <P>
070: * The actions string is converted to lowercase before processing.
071: * <P>
072: * Care should be taken before granting code permission to access
073: * certain system properties. For example, granting permission to
074: * access the "java.home" system property gives potentially malevolent
075: * code sensitive information about the system environment (the Java
076: * installation directory). Also, granting permission to access
077: * the "user.name" and "user.home" system properties gives potentially
078: * malevolent code sensitive information about the user environment
079: * (the user's account name and home directory).
080: *
081: * @see java.security.BasicPermission
082: * @see java.security.Permission
083: * @see java.security.Permissions
084: * @see java.security.PermissionCollection
085: * @see java.lang.SecurityManager
086: *
087: * @version 1.24 00/02/02
088: *
089: * @author Roland Schemers
090: * @since 1.2
091: *
092: * @serial exclude
093: */
094:
095: public final class PropertyPermission extends BasicPermission {
096:
097: /**
098: * Read action.
099: */
100: private final static int READ = 0x1;
101:
102: /**
103: * Write action.
104: */
105: private final static int WRITE = 0x2;
106: /**
107: * All actions (read,write);
108: */
109: private final static int ALL = READ | WRITE;
110: /**
111: * No actions.
112: */
113: private final static int NONE = 0x0;
114:
115: /**
116: * The actions mask.
117: *
118: */
119: private transient int mask;
120:
121: /**
122: * The actions string.
123: *
124: * @serial
125: */
126: private String actions; // Left null as long as possible, then
127:
128: // created and re-used in the getAction function.
129:
130: /**
131: * initialize a PropertyPermission object. Common to all constructors.
132: * Also called during de-serialization.
133: *
134: * @param mask the actions mask to use.
135: *
136: */
137:
138: private void init(int mask) {
139:
140: if ((mask & ALL) != mask)
141: throw new IllegalArgumentException("invalid actions mask");
142:
143: if (mask == NONE)
144: throw new IllegalArgumentException("invalid actions mask");
145:
146: if (getName() == null)
147: throw new NullPointerException("name can't be null");
148:
149: this .mask = mask;
150: }
151:
152: /**
153: * Creates a new PropertyPermission object with the specified name.
154: * The name is the name of the system property, and
155: * <i>actions</i> contains a comma-separated list of the
156: * desired actions granted on the property. Possible actions are
157: * "read" and "write".
158: *
159: * @param name the name of the PropertyPermission.
160: * @param actions the actions string.
161: */
162:
163: public PropertyPermission(String name, String actions) {
164: super (name, actions);
165: init(getMask(actions));
166: }
167:
168: /**
169: * Checks if this PropertyPermission object "implies" the specified
170: * permission.
171: * <P>
172: * More specifically, this method returns true if:<p>
173: * <ul>
174: * <li> <i>p</i> is an instanceof PropertyPermission,<p>
175: * <li> <i>p</i>'s actions are a subset of this
176: * object's actions, and <p>
177: * <li> <i>p</i>'s name is implied by this object's
178: * name. For example, "java.*" implies "java.home".
179: * </ul>
180: * @param p the permission to check against.
181: *
182: * @return true if the specified permission is implied by this object,
183: * false if not.
184: */
185: public boolean implies(Permission p) {
186: if (!(p instanceof PropertyPermission))
187: return false;
188:
189: PropertyPermission that = (PropertyPermission) p;
190:
191: // we get the effective mask. i.e., the "and" of this and that.
192: // They must be equal to that.mask for implies to return true.
193:
194: return ((this .mask & that.mask) == that.mask)
195: && super .implies(that);
196: }
197:
198: /**
199: * Checks two PropertyPermission objects for equality. Checks that <i>obj</i> is
200: * a PropertyPermission, and has the same name and actions as this object.
201: * <P>
202: * @param obj the object we are testing for equality with this object.
203: * @return true if obj is a PropertyPermission, and has the same name and
204: * actions as this PropertyPermission object.
205: */
206: public boolean equals(Object obj) {
207: if (obj == this )
208: return true;
209:
210: if (!(obj instanceof PropertyPermission))
211: return false;
212:
213: PropertyPermission that = (PropertyPermission) obj;
214:
215: return (this .mask == that.mask)
216: && (this .getName().equals(that.getName()));
217: }
218:
219: /**
220: * Returns the hash code value for this object.
221: * The hash code used is the hash code of this permissions name, that is,
222: * <code>getName().hashCode()</code>, where <code>getName</code> is
223: * from the Permission superclass.
224: *
225: * @return a hash code value for this object.
226: */
227:
228: public int hashCode() {
229: return this .getName().hashCode();
230: }
231:
232: /**
233: * Converts an actions String to an actions mask.
234: *
235: * @param action the action string.
236: * @return the actions mask.
237: */
238: private static int getMask(String actions) {
239:
240: int mask = NONE;
241:
242: if (actions == null) {
243: return mask;
244: }
245:
246: // Check against use of constants (used heavily within the JDK)
247: if (actions == SecurityConstants.PROPERTY_READ_ACTION) {
248: return READ;
249: }
250: if (actions == SecurityConstants.PROPERTY_WRITE_ACTION) {
251: return WRITE;
252: } else if (actions == SecurityConstants.PROPERTY_RW_ACTION) {
253: return READ | WRITE;
254: }
255:
256: char[] a = actions.toCharArray();
257:
258: int i = a.length - 1;
259: if (i < 0)
260: return mask;
261:
262: while (i != -1) {
263: char c;
264:
265: // skip whitespace
266: while ((i != -1)
267: && ((c = a[i]) == ' ' || c == '\r' || c == '\n'
268: || c == '\f' || c == '\t'))
269: i--;
270:
271: // check for the known strings
272: int matchlen;
273:
274: if (i >= 3 && (a[i - 3] == 'r' || a[i - 3] == 'R')
275: && (a[i - 2] == 'e' || a[i - 2] == 'E')
276: && (a[i - 1] == 'a' || a[i - 1] == 'A')
277: && (a[i] == 'd' || a[i] == 'D')) {
278: matchlen = 4;
279: mask |= READ;
280:
281: } else if (i >= 4 && (a[i - 4] == 'w' || a[i - 4] == 'W')
282: && (a[i - 3] == 'r' || a[i - 3] == 'R')
283: && (a[i - 2] == 'i' || a[i - 2] == 'I')
284: && (a[i - 1] == 't' || a[i - 1] == 'T')
285: && (a[i] == 'e' || a[i] == 'E')) {
286: matchlen = 5;
287: mask |= WRITE;
288:
289: } else {
290: // parse error
291: throw new IllegalArgumentException(
292: "invalid permission: " + actions);
293: }
294:
295: // make sure we didn't just match the tail of a word
296: // like "ackbarfaccept". Also, skip to the comma.
297: boolean seencomma = false;
298: while (i >= matchlen && !seencomma) {
299: switch (a[i - matchlen]) {
300: case ',':
301: seencomma = true;
302: /*FALLTHROUGH*/
303: case ' ':
304: case '\r':
305: case '\n':
306: case '\f':
307: case '\t':
308: break;
309: default:
310: throw new IllegalArgumentException(
311: "invalid permission: " + actions);
312: }
313: i--;
314: }
315:
316: // point i at the location of the comma minus one (or -1).
317: i -= matchlen;
318: }
319:
320: return mask;
321: }
322:
323: /**
324: * Return the canonical string representation of the actions.
325: * Always returns present actions in the following order:
326: * read, write.
327: *
328: * @return the canonical string representation of the actions.
329: */
330: static String getActions(int mask) {
331: StringBuffer sb = new StringBuffer();
332: boolean comma = false;
333:
334: if ((mask & READ) == READ) {
335: comma = true;
336: sb.append("read");
337: }
338:
339: if ((mask & WRITE) == WRITE) {
340: if (comma)
341: sb.append(',');
342: else
343: comma = true;
344: sb.append("write");
345: }
346: return sb.toString();
347: }
348:
349: /**
350: * Returns the "canonical string representation" of the actions.
351: * That is, this method always returns present actions in the following order:
352: * read, write. For example, if this PropertyPermission object
353: * allows both write and read actions, a call to <code>getActions</code>
354: * will return the string "read,write".
355: *
356: * @return the canonical string representation of the actions.
357: */
358: public String getActions() {
359: if (actions == null)
360: actions = getActions(this .mask);
361:
362: return actions;
363: }
364:
365: /**
366: * Return the current action mask.
367: * Used by the PropertyPermissionCollection
368: *
369: * @return the actions mask.
370: */
371:
372: int getMask() {
373: return mask;
374: }
375:
376: /**
377: * Returns a new PermissionCollection object for storing
378: * PropertyPermission objects.
379: * <p>
380: *
381: * @return a new PermissionCollection object suitable for storing
382: * PropertyPermissions.
383: */
384:
385: public PermissionCollection newPermissionCollection() {
386: return new PropertyPermissionCollection();
387: }
388:
389: /**
390: * WriteObject is called to save the state of the PropertyPermission
391: * to a stream. The actions are serialized, and the superclass
392: * takes care of the name.
393: */
394:
395: private synchronized void writeObject(java.io.ObjectOutputStream s)
396: throws IOException {
397: // Write out the actions. The superclass takes care of the name
398: // call getActions to make sure actions field is initialized
399: if (actions == null)
400: getActions();
401: s.defaultWriteObject();
402: }
403:
404: /**
405: * readObject is called to restore the state of the PropertyPermission from
406: * a stream.
407: */
408: private synchronized void readObject(java.io.ObjectInputStream s)
409: throws IOException, ClassNotFoundException {
410: // Read in the action, then initialize the rest
411: s.defaultReadObject();
412: init(getMask(actions));
413: }
414: }
415:
416: /**
417: * A PropertyPermissionCollection stores a set of PropertyPermission
418: * permissions.
419: *
420: * @see java.security.Permission
421: * @see java.security.Permissions
422: * @see java.security.PermissionCollection
423: *
424: * @version 1.24, 02/02/00
425: *
426: * @author Roland Schemers
427: *
428: * @serial include
429: */
430: final class PropertyPermissionCollection extends PermissionCollection
431: implements Serializable {
432:
433: /**
434: * Key is property name; value is PropertyPermission.
435: * Not serialized; see serialization section at end of class.
436: */
437: private transient Map perms;
438:
439: /**
440: * Boolean saying if "*" is in the collection.
441: *
442: * @see #serialPersistentFields
443: */
444: private boolean all_allowed;
445:
446: /**
447: * Create an empty PropertyPermissions object.
448: *
449: */
450:
451: public PropertyPermissionCollection() {
452: perms = new HashMap(32); // Capacity for default policy
453: all_allowed = false;
454: }
455:
456: /**
457: * Adds a permission to the PropertyPermissions. The key for the hash is
458: * the name.
459: *
460: * @param permission the Permission object to add.
461: *
462: * @exception IllegalArgumentException - if the permission is not a
463: * PropertyPermission
464: *
465: * @exception SecurityException - if this PropertyPermissionCollection
466: * object has been marked readonly
467: */
468:
469: public void add(Permission permission) {
470: if (!(permission instanceof PropertyPermission))
471: throw new IllegalArgumentException("invalid permission: "
472: + permission);
473: if (isReadOnly())
474: throw new SecurityException(
475: "attempt to add a Permission to a readonly PermissionCollection");
476:
477: PropertyPermission pp = (PropertyPermission) permission;
478:
479: PropertyPermission existing = (PropertyPermission) perms.get(pp
480: .getName());
481:
482: // No need to synchronize because all adds are done sequentially
483: // before any implies() calls
484:
485: if (existing != null) {
486: int oldMask = existing.getMask();
487: int newMask = pp.getMask();
488: if (oldMask != newMask) {
489: int effective = oldMask | newMask;
490: String actions = PropertyPermission
491: .getActions(effective);
492: perms.put(pp.getName(), new PropertyPermission(pp
493: .getName(), actions));
494:
495: }
496: } else {
497: perms.put(pp.getName(), permission);
498: }
499:
500: if (!all_allowed) {
501: if (pp.getName().equals("*"))
502: all_allowed = true;
503: }
504: }
505:
506: /**
507: * Check and see if this set of permissions implies the permissions
508: * expressed in "permission".
509: *
510: * @param p 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: if (!(permission instanceof PropertyPermission))
518: return false;
519:
520: PropertyPermission pp = (PropertyPermission) permission;
521: PropertyPermission x;
522:
523: int desired = pp.getMask();
524: int effective = 0;
525:
526: // short circuit if the "*" Permission was added
527: if (all_allowed) {
528: x = (PropertyPermission) perms.get("*");
529: if (x != null) {
530: effective |= x.getMask();
531: if ((effective & desired) == desired)
532: return true;
533: }
534: }
535:
536: // strategy:
537: // Check for full match first. Then work our way up the
538: // name looking for matches on a.b.*
539:
540: String name = pp.getName();
541: //System.out.println("check "+name);
542:
543: x = (PropertyPermission) perms.get(name);
544:
545: if (x != null) {
546: // we have a direct hit!
547: effective |= x.getMask();
548: if ((effective & desired) == desired)
549: return true;
550: }
551:
552: // work our way up the tree...
553: int last, offset;
554:
555: offset = name.length() - 1;
556:
557: while ((last = name.lastIndexOf(".", offset)) != -1) {
558:
559: name = name.substring(0, last + 1) + "*";
560: //System.out.println("check "+name);
561: x = (PropertyPermission) perms.get(name);
562:
563: if (x != null) {
564: effective |= x.getMask();
565: if ((effective & desired) == desired)
566: return true;
567: }
568: offset = last - 1;
569: }
570:
571: // we don't have to check for "*" as it was already checked
572: // at the top (all_allowed), so we just return false
573: return false;
574: }
575:
576: /**
577: * Returns an enumeration of all the PropertyPermission objects in the
578: * container.
579: *
580: * @return an enumeration of all the PropertyPermission objects.
581: */
582:
583: public Enumeration elements() {
584: // Convert Iterator of Map values into an Enumeration
585: return Collections.enumeration(perms.values());
586: }
587:
588: private static final long serialVersionUID = 7015263904581634791L;
589:
590: // Need to maintain serialization interoperability with earlier releases,
591: // which had the serializable field:
592: //
593: // Table of permissions.
594: //
595: // @serial
596: //
597: // private Hashtable permissions;
598: /**
599: * @serialField permissions java.util.Hashtable
600: * A table of the PropertyPermissions.
601: * @serialField all_allowed boolean
602: * boolean saying if "*" is in the collection.
603: */
604: private static final ObjectStreamField[] serialPersistentFields = {
605: new ObjectStreamField("permissions", Hashtable.class),
606: new ObjectStreamField("all_allowed", Boolean.TYPE), };
607:
608: /**
609: * @serialData Default fields.
610: */
611: /*
612: * Writes the contents of the perms field out as a Hashtable for
613: * serialization compatibility with earlier releases. all_allowed
614: * unchanged.
615: */
616: private void writeObject(ObjectOutputStream out) throws IOException {
617: // Don't call out.defaultWriteObject()
618:
619: // Copy perms into a Hashtable
620: Hashtable permissions = new Hashtable(perms.size() * 2);
621: permissions.putAll(perms);
622:
623: // Write out serializable fields
624: ObjectOutputStream.PutField pfields = out.putFields();
625: pfields.put("all_allowed", all_allowed);
626: pfields.put("permissions", permissions);
627: out.writeFields();
628: }
629:
630: /*
631: * Reads in a Hashtable of PropertyPermissions and saves them in the
632: * perms field. Reads in all_allowed.
633: */
634: private void readObject(ObjectInputStream in) throws IOException,
635: ClassNotFoundException {
636: // Don't call defaultReadObject()
637:
638: // Read in serialized fields
639: ObjectInputStream.GetField gfields = in.readFields();
640:
641: // Get all_allowed
642: all_allowed = gfields.get("all_allowed", false);
643:
644: // Get permissions
645: Hashtable permissions = (Hashtable) gfields.get("permissions",
646: null);
647: perms = new HashMap(permissions.size() * 2);
648: perms.putAll(permissions);
649: }
650: }
|