home *** CD-ROM | disk | FTP | other *** search
Java Source | 1998-03-20 | 22.8 KB | 873 lines |
- /*
- * @(#)SocketPermission.java 1.10 98/03/18
- *
- * Copyright 1997, 1998 by Sun Microsystems, Inc.,
- * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
- * All rights reserved.
- *
- * This software is the confidential and proprietary information
- * of Sun Microsystems, Inc. ("Confidential Information"). You
- * shall not disclose such Confidential Information and shall use
- * it only in accordance with the terms of the license agreement
- * you entered into with Sun.
- */
-
- package java.net;
-
- import java.util.Enumeration;
- import java.util.Vector;
- import java.util.Hashtable;
- import java.util.StringTokenizer;
- import java.net.InetAddress;
- import java.security.Permission;
- import java.security.PermissionCollection;
- import java.io.Serializable;
- import java.io.IOException;
-
- /**
- * This class represents access to a network via sockets.
- * A SocketPermission consists of a
- * host specification and a set of "actions" specifying ways to
- * connect to that host. The host is specified as
- * <pre>
- * host = (hostname | IPaddress)[:portrange]
- * portrange = portnumber | portnumber-[portnumber]
- * </pre>
- * The possible ways to connect to the host are
- * <pre>
- * accept
- * connect
- * listen
- * resolve
- * </pre>
- * The "listen" action is only meaningful when used with "localhost".
- * The "resolve" (resolve host/ip name service lookups) action is implied
- * when any of the other actions are present.
- *
- * <p>As an example of the creation and meaning of SocketPermissions, if the
- * following permissions are created for classes
- * signed by mrm:
- *
- * <pre>
- * p1 = new SocketPermission("puffin.eng.sun.com:7777", "connect,accept");
- * p2 = new SocketPermission("localhost:1024-", "accept,connect,listen");
- * </pre>
- *
- * then classes signedBy mrm can connect to port 7777 on
- * <code>puffin.eng.sun.com</code>, or accept connections on that port.
- * Classes signedBy mrm may also listen on any port between 1024 and
- * 65535, as well as port 0.
- *
- * @see Permissions
- * @see SocketPermissions
- *
- * @version 1.10 98/03/18
- *
- * @author Marianne Mueller
- * @author Roland Schemers
- */
-
- public final class SocketPermission extends Permission
- implements java.io.Serializable
- {
-
- /** use serialVersionUID from JDK 1.2 for interoperability */
- private static final long serialVersionUID = -323680778045894697L;
-
- /**
- * Connect to host:port
- */
- private final static int CONNECT = 0x1;
-
- /**
- * Listen on host:port
- */
- private final static int LISTEN = 0x2;
-
- /**
- * Accept a connection from host:port
- */
- private final static int ACCEPT = 0x4;
-
- /**
- * Resolve DNS queries
- */
- private final static int RESOLVE = 0x8;
-
- /**
- * No actions
- */
- private final static int NONE = 0x0;
-
- /**
- * All actions
- */
- private final static int ALL = CONNECT|LISTEN|ACCEPT|RESOLVE;
-
- // various port constants
- private static final int PORT_MIN = 0;
- private static final int PORT_MAX = 65535;
- private static final int PRIV_PORT_MAX = 1023;
-
- // the actions mask
- private int mask;
-
- // the actions string. Left null as long as possible, then
- // created and re-used in the getAction function.
- private String actions;
-
- // the canonical name of the host
- // in the case of "*.foo.com", cname is ".foo.com".
-
- private String cname;
-
- // all the IP addresses of the host
- private InetAddress[] addresses;
-
- // true if the hostname is a wildcard (e.g. "*.sun.com")
- private boolean wildcard;
-
- // true if we were initialized with a single numeric IP address
- private boolean init_with_ip;
-
- // true if this SocketPermission represents an invalid/unknown host
- // used for implies when the delayed lookup has already failed
- private boolean invalid;
-
- // port range on host
- private int[] portrange;
-
- /**
- * Creates a new SocketPermission object with the specified actions.
- * The host is expressed as a DNS name, or as a numerical IP address.
- * Optionally, a port or a portrange may be supplied (separated
- * from the DNS name or IP address by a colon).
- * <p>
- * The <i>actions</i> parameter contains a comma-separated list of the
- * actions granted for the specified host (and port(s)). Possible actions are
- * "connect", "listen", "accept", "resolve", or
- * any combination of those. "resolve" is automatically added
- * when any of the other three are specified.
- * <p>
- * Examples of SocketPermission instantiation are the following:
- * <pre>
- * nr = new SocketPermission("www.catalog.com", "connect");
- * nr = new SocketPermission("www.sun.com:80", "connect");
- * nr = new SocketPermission("*.sun.com", "connect");
- * nr = new SocketPermission("*.edu", "resolve");
- * nr = new SocketPermission("204.160.241.0", "connect");
- * nr = new SocketPermission("localhost:1024-65535", "listen");
- * nr = new SocketPermission("204.160.241.0:1024-65535", "connect");
- * </pre>
- *
- * @param host the hostname or IPaddress of the computer, optionally
- * including a colon followed by a port or port range.
- * @param action the action string.
- */
- public SocketPermission(String host, String action) {
- super(host);
- init(host, getMask(action));
- }
-
-
- SocketPermission(String host, int mask) {
- super(host);
- init(host, mask);
- }
-
- private boolean isDottedIP(String host)
- {
- int n = 0;
-
- for(int i = 0; i < host.length(); i++) {
-
- char c = host.charAt(i);
- if (!Character.isDigit(c)) { // !digit
- return false;
- }
- int b = 0;
- while(c != '.') {
- if (!Character.isDigit(c)) {
- return false;
- }
- b = b*10 + c - '0';
- if (++i >= host.length()) {
- break;
- }
- c = host.charAt(i);
- }
- if (b > 0xFF) { /* bogus - bigger than a byte */
- return false;
- }
- n++;
- }
-
- if (n != 4) {
- return false;
- } else {
- return true;
- }
- }
-
- private int[] parsePort(String port)
- throws Exception
- {
-
- if (port == null || port.equals("") || port.equals("*")) {
- return new int[] {PORT_MIN, PORT_MAX};
- }
-
- int dash = port.indexOf('-');
-
- if (dash == -1) {
- int p = Integer.parseInt(port);
- return new int[] {p, p};
- } else {
- String low = port.substring(0, dash);
- String high = port.substring(dash+1);
- int l,h;
-
- if (low.equals("")) {
- l = PORT_MIN;
- } else {
- l = Integer.parseInt(low);
- }
-
- if (high.equals("")) {
- h = PORT_MAX;
- } else {
- h = Integer.parseInt(high);
- }
- if (h<l)
- throw new IllegalArgumentException("invalid port range");
-
- return new int[] {l, h};
- }
- }
-
- /**
- * Initialize the SocketPermission object. We don't do any DNS lookups
- * as this point, instead we hold off until the implies method is
- * called.
- */
- private void init(String host, int mask) {
-
- // Set the integer mask that represents the actions
-
- if ((mask & ALL) != mask)
- throw new IllegalArgumentException("invalid actions mask");
-
- // always OR in RESOLVE if we allow any of the others
- this.mask = mask | RESOLVE;
-
- // Parse the host name. A name has up to three components, the
- // hostname, a port number, or two numbers representing a port
- // range. "www.sun.com:8080-9090" is a valid host name.
-
- int sep = host.indexOf(':');
-
- if (sep != -1) {
- String port = host.substring(sep+1);
- host = host.substring(0, sep);
- try {
- portrange = parsePort(port);
- } catch (Exception e) {
- throw new
- IllegalArgumentException("invalid port range: "+port);
- }
- } else {
- portrange = new int[] { PORT_MIN, PORT_MAX };
- }
-
- // is this a domain wildcard specification
-
- if (host.startsWith("*")) {
- wildcard = true;
- if (host.equals("*")) {
- cname = "";
- } else if (host.startsWith("*.")) {
- cname = host.substring(1).toLowerCase();
- } else {
- throw new
- IllegalArgumentException("invalid host wildcard specification");
- }
- return;
- } else {
- // see if we are being initialized with an IP address.
- if (isDottedIP(host)) {
- try {
- addresses =
- new InetAddress[] {InetAddress.getByName(host) };
- init_with_ip = true;
- } catch (UnknownHostException uhe) {
- // this shouldn't happen
- invalid = true;
- }
- }
- }
- }
-
- /**
- * Convert an action string to an integer actions mask.
- *
- * @param action the action string
- * @return the action mask
- */
- private static int getMask(String action) {
-
- int mask = NONE;
-
- if (action == null) {
- return mask;
- }
-
- action = action.toLowerCase();
- StringTokenizer st = new StringTokenizer(action, ",");
- while (st.hasMoreTokens()) {
- String token = st.nextToken().trim();
- if (token.equals("connect"))
- mask |= CONNECT;
- else if (token.equals("listen"))
- mask |= LISTEN;
- else if (token.equals("accept"))
- mask |= ACCEPT;
- else if (token.equals("resolve"))
- mask |= RESOLVE;
- else
- throw new
- IllegalArgumentException("invalid net permission: "+token);
- }
- return mask;
- }
-
-
- /**
- * attempt to get the fully qualified domain name
- *
- */
- void getCanonName()
- throws UnknownHostException
- {
- if (cname != null || invalid) return;
-
- // attempt to get the canonical name
-
- try {
- // first get the IP addresses if we don't have them yet
- // this is because we need the IP address to then get
- // FQDN.
- if (addresses == null) {
- getIP();
- }
-
- // we have to do this check, otherwise we might not
- // get the fully qualified domain name
- if (init_with_ip) {
- cname = addresses[0].getHostName(false).toLowerCase();
- } else {
- cname = InetAddress.getByName(addresses[0].getHostAddress()).
- getHostName(false).toLowerCase();
- }
- } catch (UnknownHostException uhe) {
- invalid = true;
- throw uhe;
- }
- }
-
- /**
- * get IP addresses. Sets invalid to true if we can't get them.
- *
- */
- void getIP()
- throws UnknownHostException
- {
- if (addresses != null || wildcard || invalid) return;
-
- try {
- // now get all the IP addresses
- String host;
- int i = getName().indexOf(":");
- if (i == -1)
- host = getName();
- else {
- host = getName().substring(0,i);
- }
-
- addresses =
- InetAddress.getAllByName0(host, false);
-
- } catch (UnknownHostException uhe) {
- invalid = true;
- throw uhe;
- }
- }
-
- /**
- * Checks if this socket permission object "implies" the
- * specified permission.
- * <P>
- * More specifically, this method first ensures that all of the following
- * are true (and returns false if any of them are not):<p>
- * <ul>
- * <li> <i>p</i> is an instanceof SocketPermission,<p>
- * <li> <i>p</i>'s actions are a proper subset of this
- * object's actions, and<p>
- * <li> <i>p</i>'s port range is included in this port range.<p>
- * </ul>
- *
- * Then <code>implies</code> checks each of the following, in order,
- * and for each returns true if the stated condition is true:<p>
- * <ul>
- * <li> If this object was initialized with a single IP address and one of <i>p</i>'s
- * IP addresses is equal to this object's IP address.<p>
- * <li>If this object is a wildcard domain (such as *.sun.com), and
- * <i>p</i>'s canonical name (the name without any preceding *)
- * ends with this object's canonical host name. For example, *.sun.com
- * implies *.eng.sun.com..<p>
- * <li>If this object was not initialized with a single IP address, and one of this
- * object's IP addresses equals one of <i>p</i>'s IP addresses.<p>
- * <li>If this canonical name equals <i>p</i>'s canonical name.<p>
- * </ul>
- *
- * If none of the above are true, <code>implies</code> returns false.
- * @param p the permission to check against.
- *
- * @return true if the specified permission is implied by this object,
- * false if not.
- */
-
- public boolean implies(Permission p) {
- int i,j;
-
- if (!(p instanceof SocketPermission))
- return false;
-
- SocketPermission that = (SocketPermission) p;
-
- return ((this.mask & that.mask) == that.mask) &&
- impliesIgnoreMask(that);
- }
-
- /**
- * Checks if the incoming Permission's action are a proper subset of
- * the this object's actions.
- * <P>
- * Check, in the following order:
- * <ul>
- * <li> Checks that "p" is an instanceof a SocketPermission
- * <li> Checks that "p"'s actions are a proper subset of the
- * current object's actions.
- * <li> Checks that "p"'s port range is included in this port range
- * <li> If this object was initialized with an IP address, checks that
- * one of "p"'s IP addresses is equal to this object's IP address.
- * <li> If either object is a wildcard domain (i.e., "*.sun.com"),
- * attempt to match based on the wildcard.
- * <li> If this object was not initialized with an IP address, attempt
- * to find a match based on the IP addresses in both objects.
- * <li> Attempt to match on the canonical hostnames of both objects.
- * </ul>
- * @param p the incoming permission request
- *
- * @return true if "permission" is a proper subset of the current object,
- * false if not.
- */
-
- boolean impliesIgnoreMask(SocketPermission that) {
- int i,j;
-
- if ((that.mask & RESOLVE) != that.mask) {
- // check port range
- if ((that.portrange[0] < this.portrange[0]) ||
- (that.portrange[1] > this.portrange[1])) {
- return false;
- }
- }
-
- // allow a "*" wildcard to always match anything
- if (this.wildcard && this.getName().equals("*"))
- return true;
-
- // return if either one of these NetPerm objects are invalid...
- if (this.invalid || that.invalid)
- return false;
-
-
- try {
- if (this.init_with_ip) { // we only check IP addresses
- if (that.wildcard)
- return false;
-
- if (that.init_with_ip) {
- return (this.addresses[0].equals(that.addresses[0]));
- } else {
- if (that.addresses == null) {
- that.getIP();
- }
- for (i=0; i < that.addresses.length; i++) {
- if (this.addresses[0].equals(that.addresses[i]))
- return true;
- }
- }
- // since "this" was initialized with an IP address, we
- // don't check any other cases
- return false;
- }
-
- // check and see if we have any wildcards...
- if (this.wildcard || that.wildcard) {
- // if they are both wildcards, return true iff
- // that's cname ends with this cname (i.e., *.sun.com
- // implies *.eng.sun.com)
- if (this.wildcard && that.wildcard)
- return (that.cname.endsWith(this.cname));
-
- // a non-wildcard can't imply a wildcard
- if (that.wildcard)
- return false;
-
- // this is a wildcard, lets see if that's cname ends with
- // it...
- if (that.cname == null) {
- that.getCanonName();
- }
- return (that.cname.endsWith(this.cname));
- }
-
- // comapare IP addresses
- if (this.addresses == null) {
- this.getIP();
- }
-
- if (that.addresses == null) {
- that.getIP();
- }
-
- for (j = 0; j < this.addresses.length; j++) {
- for (i=0; i < that.addresses.length; i++) {
- if (this.addresses[j].equals(that.addresses[i]))
- return true;
- }
- }
-
- // XXX: if all else fails, compare hostnames?
- // Do we really want this?
- if (this.cname == null) {
- this.getCanonName();
- }
-
- if (that.cname == null) {
- that.getCanonName();
- }
-
- return (this.cname.equals(that.cname));
-
- } catch (UnknownHostException uhe) {
- // commented out, otherwise the last return is
- // flagged as unreachable by the compiler.
- // return false;
- }
-
- // make sure the first thing that is done here is to return
- // false. If not, uncomment the return false in the above catch.
-
- return false;
- }
-
- /**
- * Checks two SocketPermission objects for equality.
- * <P>
- * @param obj the object to test for equality with this object.
- *
- * @return true if <i>obj</i> is a SocketPermission, and has the same hostname and
- * actions as this SocketPermission object.
- */
- public boolean equals(Object obj) {
- if (obj == this)
- return true;
-
- if (! (obj instanceof SocketPermission))
- return false;
-
- SocketPermission that = (SocketPermission) obj;
-
- //this is (overly?) complex!!!
-
- // check the mask first
- if (this.mask != that.mask) return false;
-
- // now check the port range...
- if ((this.portrange[0] != that.portrange[0]) ||
- (this.portrange[1] != that.portrange[1])) {
- return false;
- }
-
- // short cut. This catches:
- // "crypto" equal to "crypto", or
- // "1.2.3.4" equal to "1.2.3.4.", or
- // "*.edu" equal to "*.edu", but it
- // does not catch "crypto" equal to
- // "crypto.eng.sun.com".
-
- if (this.getName().equals(that.getName())) {
- return true;
- }
-
- // we now attempt to get the Canonical (FQDN) name and
- // compare that. If this fails, about all we can do is return
- // false.
-
- try {
- this.getCanonName();
- that.getCanonName();
- } catch (UnknownHostException uhe) {
- return false;
- }
-
- if (this.invalid || that.invalid)
- return false;
-
- if (this.cname != null) {
- return this.cname.equals(that.cname);
- }
-
- return false;
- }
-
- /**
- * Returns the hash code value for this object.
- *
- * @return a hash code value for this object.
- */
-
- public int hashCode() {
- /*
- * If this SocketPermission was initialized with an IP address
- * or a wildcard, use getName().hashCode(), otherwise use
- * the hashCode() of the host name returned from
- * java.net.InetAddress.getHostName method.
- */
-
- if (init_with_ip || wildcard) {
- return this.getName().hashCode();
- }
-
- try {
- getCanonName();
- } catch (UnknownHostException uhe) {
-
- }
-
- if (invalid || cname == null)
- return this.getName().hashCode();
- else
- return this.cname.hashCode();
- }
-
- /**
- * Return the current action mask.
- *
- * @return the actions mask.
- */
-
- int getMask() {
- return mask;
- }
-
- /**
- * Returns the "canonical string representation" of the actions in the
- * specified mask.
- * Always returns present actions in the following order:
- * connect, listen, accept, resolve.
- *
- * @param mask a specific integer action mask to translate into a string
- * @return the canonical string representation of the actions
- */
- private static String getActions(int mask)
- {
- StringBuffer sb = new StringBuffer();
- boolean comma = false;
-
- if ((mask & CONNECT) == CONNECT) {
- comma = true;
- sb.append("connect");
- }
-
- if ((mask & LISTEN) == LISTEN) {
- if (comma) sb.append(',');
- else comma = true;
- sb.append("listen");
- }
-
- if ((mask & ACCEPT) == ACCEPT) {
- if (comma) sb.append(',');
- else comma = true;
- sb.append("accept");
- }
-
-
- if ((mask & RESOLVE) == RESOLVE) {
- if (comma) sb.append(',');
- else comma = true;
- sb.append("resolve");
- }
-
- return sb.toString();
- }
-
- /**
- * Returns the canonical string representation of the actions.
- * Always returns present actions in the following order:
- * connect, listen, accept, resolve.
- *
- * @return the canonical string representation of the actions.
- */
- public String getActions()
- {
- if (actions == null)
- actions = getActions(this.mask);
-
- return actions;
- }
-
- /**
- * Returns a new PermissionCollection object for storing SocketPermission
- * objects.
- * <p>
- * SocketPermission objects must be stored in a manner that allows them
- * to be inserted into the collection in any order, but that also enables the
- * PermissionCollection <code>implies</code>
- * method to be implemented in an efficient (and consistent) manner.
- *
- * @return a new PermissionCollection object suitable for storing SocketPermissions.
- */
-
- public PermissionCollection newPermissionCollection() {
- return new SocketPermissionCollection();
- }
-
- /**
- * WriteObject is called to save the state of the SocketPermission
- * to a stream. Only the mask is serialized since we want to
- * recalculate the other values when the contents are restored.
- */
- private synchronized void writeObject(java.io.ObjectOutputStream s)
- throws IOException
- {
- // Write out the mask. The superclass takes care of the name
- s.writeInt(mask);
- }
-
- /**
- * readObject is called to restore the state of the FilePermission from
- * a stream. init is called to initialize the rest of the values.
- */
- private synchronized void readObject(java.io.ObjectInputStream s)
- throws IOException, ClassNotFoundException
- {
- // Read in the mask, then initialize the rest
- mask = s.readInt();
- init(getName(),mask);
- }
- }
-
- /**
-
- if (init'd with IP, key is IP as string)
- if wildcard, its the wild card
- else its the cname?
-
- *
- * @see java.security.Permission
- * @see java.security.Permissions
- * @see java.security.PermissionCollection
- *
- * @version 1.10 98/03/18
- *
- * @author Roland Schemers
- */
-
- final class SocketPermissionCollection extends PermissionCollection
- implements Serializable
- {
- /** use serialVersionUID from JDK 1.2 for interoperability */
- private static final long serialVersionUID = 2787186408602843674L;
-
- /**
- * The SocketPermissions for this set.
- */
-
- private Vector permissions;
-
- /**
- * Create an empty SocketPermissions object.
- *
- */
-
- public SocketPermissionCollection() {
- permissions = new Vector();
- }
-
- /**
- * Adds a permission to the SocketPermissions. The key for the hash is
- * the name in the case of wildcards, or all the IP addresses.
- *
- * @param permission the Permission object to add.
- */
-
- public void add(Permission permission)
- {
- if (! (permission instanceof SocketPermission))
- throw new IllegalArgumentException("invalid permission: "+
- permission);
- permissions.addElement(permission);
- }
-
- /**
- * Check and see if this collection of permissions implies the permissions
- * expressed in "permission".
- *
- * @param p the Permission object to compare
- *
- * @return true if "permission" is a proper subset of a permission in
- * the collection, false if not.
- */
-
- public boolean implies(Permission permission)
- {
- if (! (permission instanceof SocketPermission))
- return false;
-
- SocketPermission np = (SocketPermission) permission;
-
- int desired = np.getMask();
- int effective = 0;
- int needed = desired;
-
- Enumeration e = permissions.elements();
- //System.out.println("implies "+np);
- while (e.hasMoreElements()) {
- SocketPermission x = (SocketPermission) e.nextElement();
- //System.out.println(" trying "+x);
- if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(np)) {
- effective |= x.getMask();
- if ((effective & desired) == desired)
- return true;
- needed = (desired ^ effective);
- }
- }
- return false;
- }
-
- /**
- * Returns an enumeration of all the SocketPermission objects in the
- * container.
- *
- * @return an enumeration of all the SocketPermission objects.
- */
-
- public Enumeration elements()
- {
- return permissions.elements();
- }
- }
-