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