0001: /*
0002: * @(#)SocketPermission.java 1.48 06/10/10
0003: *
0004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: *
0026: */
0027:
0028: package java.net;
0029:
0030: import java.util.Enumeration;
0031: import java.util.Vector;
0032: import java.util.List;
0033: import java.util.ArrayList;
0034: import java.util.Collections;
0035: import java.util.StringTokenizer;
0036: import java.net.InetAddress;
0037: import java.security.Permission;
0038: import java.security.PermissionCollection;
0039: import java.io.Serializable;
0040: import java.io.ObjectStreamField;
0041: import java.io.ObjectOutputStream;
0042: import java.io.ObjectInputStream;
0043: import java.io.IOException;
0044: import sun.security.util.SecurityConstants;
0045:
0046: /**
0047: * This class represents access to a network via sockets.
0048: * A SocketPermission consists of a
0049: * host specification and a set of "actions" specifying ways to
0050: * connect to that host. The host is specified as
0051: * <pre>
0052: * host = (hostname | IPv4address | iPv6reference) [:portrange]
0053: * portrange = portnumber | -portnumber | portnumber-[portnumber]
0054: * </pre>
0055: * The host is expressed as a DNS name, as a numerical IP address,
0056: * or as "localhost" (for the local machine).
0057: * The wildcard "*" may be included once in a DNS name host
0058: * specification. If it is included, it must be in the leftmost
0059: * position, as in "*.sun.com".
0060: * <p>
0061: * The format of the IPv6reference should follow that specified in <a
0062: * href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC 2732: Format
0063: * for Literal IPv6 Addresses in URLs</i></a>:
0064: * <pre>
0065: * ipv6reference = "[" IPv6address "]"
0066: *</pre>
0067: * For example, you can construct a SocketPermission instance
0068: * as the following:
0069: * <pre>
0070: * String hostAddress = inetaddress.getHostAddress();
0071: * if (inetaddress instanceof Inet6Address) {
0072: * sp = new SocketPermission("[" + hostAddress + "]:" + port, action);
0073: * } else {
0074: * sp = new SocketPermission(hostAddress + ":" + port, action);
0075: * }
0076: * </pre>
0077: * or
0078: * <pre>
0079: * String host = url.getHost();
0080: * sp = new SocketPermission(host + ":" + port, action);
0081: * </pre>
0082: * <p>
0083: * The <A HREF="Inet6Address.html#lform">full uncompressed form</A> of
0084: * an IPv6 literal address is also valid.
0085: * <p>
0086: * The port or portrange is optional. A port specification of the
0087: * form "N-", where <i>N</i> is a port number, signifies all ports
0088: * numbered <i>N</i> and above, while a specification of the
0089: * form "-N" indicates all ports numbered <i>N</i> and below.
0090: * <p>
0091: * The possible ways to connect to the host are
0092: * <pre>
0093: * accept
0094: * connect
0095: * listen
0096: * resolve
0097: * </pre>
0098: * The "listen" action is only meaningful when used with "localhost".
0099: * The "resolve" action is implied when any of the other actions are present.
0100: * The action "resolve" refers to host/ip name service lookups.
0101: *
0102: * <p>As an example of the creation and meaning of SocketPermissions,
0103: * note that if the following permission:
0104: *
0105: * <pre>
0106: * p1 = new SocketPermission("puffin.eng.sun.com:7777", "connect,accept");
0107: * </pre>
0108: *
0109: * is granted to some code, it allows that code to connect to port 7777 on
0110: * <code>puffin.eng.sun.com</code>, and to accept connections on that port.
0111: *
0112: * <p>Similarly, if the following permission:
0113: *
0114: * <pre>
0115: * p1 = new SocketPermission("puffin.eng.sun.com:7777", "connect,accept");
0116: * p2 = new SocketPermission("localhost:1024-", "accept,connect,listen");
0117: * </pre>
0118: *
0119: * is granted to some code, it allows that code to
0120: * accept connections on, connect to, or listen on any port between
0121: * 1024 and 65535 on the local host.
0122: *
0123: * <p>Note: Granting code permission to accept or make connections to remote
0124: * hosts may be dangerous because malevolent code can then more easily
0125: * transfer and share confidential data among parties who may not
0126: * otherwise have access to the data.
0127: *
0128: * @see java.security.Permissions
0129: * @see SocketPermission
0130: *
0131: * @version 1.41 10/27/00
0132: *
0133: * @author Marianne Mueller
0134: * @author Roland Schemers
0135: *
0136: * @serial exclude
0137: */
0138:
0139: public final class SocketPermission extends Permission implements
0140: java.io.Serializable {
0141: private static final long serialVersionUID = -7204263841984476862L;
0142:
0143: /**
0144: * Connect to host:port
0145: */
0146: private final static int CONNECT = 0x1;
0147:
0148: /**
0149: * Listen on host:port
0150: */
0151: private final static int LISTEN = 0x2;
0152:
0153: /**
0154: * Accept a connection from host:port
0155: */
0156: private final static int ACCEPT = 0x4;
0157:
0158: /**
0159: * Resolve DNS queries
0160: */
0161: private final static int RESOLVE = 0x8;
0162:
0163: /**
0164: * No actions
0165: */
0166: private final static int NONE = 0x0;
0167:
0168: /**
0169: * All actions
0170: */
0171: private final static int ALL = CONNECT | LISTEN | ACCEPT | RESOLVE;
0172:
0173: // various port constants
0174: private static final int PORT_MIN = 0;
0175: private static final int PORT_MAX = 65535;
0176: private static final int PRIV_PORT_MAX = 1023;
0177:
0178: // the actions mask
0179: private transient int mask;
0180:
0181: /**
0182: * the actions string.
0183: *
0184: * @serial
0185: */
0186:
0187: private String actions; // Left null as long as possible, then
0188: // created and re-used in the getAction function.
0189:
0190: // hostname part as it is passed
0191: private transient String hostname;
0192:
0193: // the canonical name of the host
0194: // in the case of "*.foo.com", cname is ".foo.com".
0195:
0196: private transient String cname;
0197:
0198: // all the IP addresses of the host
0199: private transient InetAddress[] addresses;
0200:
0201: // true if the hostname is a wildcard (e.g. "*.sun.com")
0202: private transient boolean wildcard;
0203:
0204: // true if we were initialized with a single numeric IP address
0205: private transient boolean init_with_ip;
0206:
0207: // true if this SocketPermission represents an invalid/unknown host
0208: // used for implies when the delayed lookup has already failed
0209: private transient boolean invalid;
0210:
0211: // port range on host
0212: private transient int[] portrange;
0213:
0214: // true if the trustProxy system property is set
0215: private static boolean trustProxy;
0216:
0217: static {
0218: Boolean tmp = (Boolean) java.security.AccessController
0219: .doPrivileged(new sun.security.action.GetBooleanAction(
0220: "trustProxy"));
0221: trustProxy = tmp.booleanValue();
0222: }
0223:
0224: /**
0225: * Creates a new SocketPermission object with the specified actions.
0226: * The host is expressed as a DNS name, or as a numerical IP address.
0227: * Optionally, a port or a portrange may be supplied (separated
0228: * from the DNS name or IP address by a colon).
0229: * <p>
0230: * To specify the local machine, use "localhost" as the <i>host</i>.
0231: * Also note: An empty <i>host</i> String ("") is equivalent to "localhost".
0232: * <p>
0233: * The <i>actions</i> parameter contains a comma-separated list of the
0234: * actions granted for the specified host (and port(s)). Possible actions are
0235: * "connect", "listen", "accept", "resolve", or
0236: * any combination of those. "resolve" is automatically added
0237: * when any of the other three are specified.
0238: * <p>
0239: * Examples of SocketPermission instantiation are the following:
0240: * <pre>
0241: * nr = new SocketPermission("www.catalog.com", "connect");
0242: * nr = new SocketPermission("www.sun.com:80", "connect");
0243: * nr = new SocketPermission("*.sun.com", "connect");
0244: * nr = new SocketPermission("*.edu", "resolve");
0245: * nr = new SocketPermission("204.160.241.0", "connect");
0246: * nr = new SocketPermission("localhost:1024-65535", "listen");
0247: * nr = new SocketPermission("204.160.241.0:1024-65535", "connect");
0248: * </pre>
0249: *
0250: * @param host the hostname or IPaddress of the computer, optionally
0251: * including a colon followed by a port or port range.
0252: * @param action the action string.
0253: */
0254: public SocketPermission(String host, String action) {
0255: super (getHost(host));
0256: // name initialized to getHost(host); NPE detected in getHost()
0257: init(getName(), getMask(action));
0258: }
0259:
0260: SocketPermission(String host, int mask) {
0261: super (getHost(host));
0262: // name initialized to getHost(host); NPE detected in getHost()
0263: init(getName(), mask);
0264: }
0265:
0266: private static String getHost(String host) {
0267: if (host.equals("")) {
0268: return "localhost";
0269: } else {
0270: /* IPv6 literal address used in this context should follow
0271: * the format specified in RFC 2732;
0272: * if not, we try to solve the unambiguous case
0273: */
0274: int ind;
0275: if (host.charAt(0) != '[') {
0276: if ((ind = host.indexOf(':')) != host.lastIndexOf(':')) {
0277: /* More than one ":", meaning IPv6 address is not
0278: * in RFC 2732 format;
0279: * We will rectify user errors for all unambiguious cases
0280: */
0281: StringTokenizer st = new StringTokenizer(host, ":");
0282: int tokens = st.countTokens();
0283: if (tokens == 9) {
0284: // IPv6 address followed by port
0285: ind = host.lastIndexOf(':');
0286: host = "[" + host.substring(0, ind) + "]"
0287: + host.substring(ind);
0288: } else if (tokens == 8 && host.indexOf("::") == -1) {
0289: // IPv6 address only, not followed by port
0290: host = "[" + host + "]";
0291: } else {
0292: // could be ambiguous
0293: throw new IllegalArgumentException("Ambiguous"
0294: + " hostport part");
0295: }
0296: }
0297: }
0298: return host;
0299: }
0300: }
0301:
0302: private int[] parsePort(String port) throws Exception {
0303:
0304: if (port == null || port.equals("") || port.equals("*")) {
0305: return new int[] { PORT_MIN, PORT_MAX };
0306: }
0307:
0308: int dash = port.indexOf('-');
0309:
0310: if (dash == -1) {
0311: int p = Integer.parseInt(port);
0312: return new int[] { p, p };
0313: } else {
0314: String low = port.substring(0, dash);
0315: String high = port.substring(dash + 1);
0316: int l, h;
0317:
0318: if (low.equals("")) {
0319: l = PORT_MIN;
0320: } else {
0321: l = Integer.parseInt(low);
0322: }
0323:
0324: if (high.equals("")) {
0325: h = PORT_MAX;
0326: } else {
0327: h = Integer.parseInt(high);
0328: }
0329: if (l < 0 || h < 0 || h < l)
0330: throw new IllegalArgumentException("invalid port range");
0331:
0332: return new int[] { l, h };
0333: }
0334: }
0335:
0336: /**
0337: * Initialize the SocketPermission object. We don't do any DNS lookups
0338: * as this point, instead we hold off until the implies method is
0339: * called.
0340: */
0341: private void init(String host, int mask) {
0342: // Set the integer mask that represents the actions
0343:
0344: if ((mask & ALL) != mask)
0345: throw new IllegalArgumentException("invalid actions mask");
0346:
0347: // always OR in RESOLVE if we allow any of the others
0348: this .mask = mask | RESOLVE;
0349:
0350: // Parse the host name. A name has up to three components, the
0351: // hostname, a port number, or two numbers representing a port
0352: // range. "www.sun.com:8080-9090" is a valid host name.
0353:
0354: // With IPv6 an address can be 2010:836B:4179::836B:4179
0355: // An IPv6 address needs to be enclose in []
0356: // For ex: [2010:836B:4179::836B:4179]:8080-9090
0357: // Refer to RFC 2732 for more information.
0358:
0359: int rb = 0;
0360: int start = 0, end = 0;
0361: int sep = -1;
0362: String hostport = host;
0363: if (host.charAt(0) == '[') {
0364: start = 1;
0365: rb = host.indexOf(']');
0366: if (rb != -1) {
0367: host = host.substring(start, rb);
0368: } else {
0369: throw new IllegalArgumentException(
0370: "invalid host/port: " + host);
0371: }
0372: sep = hostport.indexOf(':', rb + 1);
0373: } else {
0374: start = 0;
0375: sep = host.indexOf(':', rb);
0376: end = sep;
0377: if (sep != -1) {
0378: host = host.substring(start, end);
0379: }
0380: }
0381:
0382: if (sep != -1) {
0383: String port = hostport.substring(sep + 1);
0384: try {
0385: portrange = parsePort(port);
0386: } catch (Exception e) {
0387: throw new IllegalArgumentException(
0388: "invalid port range: " + port);
0389: }
0390: } else {
0391: portrange = new int[] { PORT_MIN, PORT_MAX };
0392: }
0393:
0394: hostname = host;
0395:
0396: // is this a domain wildcard specification
0397: if (host.lastIndexOf('*') > 0) {
0398: throw new IllegalArgumentException(
0399: "invalid host wildcard specification");
0400: } else if (host.startsWith("*")) {
0401: wildcard = true;
0402: if (host.equals("*")) {
0403: cname = "";
0404: } else if (host.startsWith("*.")) {
0405: cname = host.substring(1).toLowerCase();
0406: } else {
0407: throw new IllegalArgumentException(
0408: "invalid host wildcard specification");
0409: }
0410: return;
0411: } else {
0412: if (host.length() > 0) {
0413: // see if we are being initialized with an IP address.
0414: char ch = host.charAt(0);
0415: if (ch == ':' || Character.digit(ch, 16) != -1) {
0416: byte ip[] = Inet4Address.textToNumericFormat(host);
0417: if (ip == null) {
0418: ip = Inet6Address.textToNumericFormat(host);
0419: }
0420: if (ip != null) {
0421: try {
0422: addresses = new InetAddress[] { InetAddress
0423: .getByAddress(ip) };
0424: init_with_ip = true;
0425: } catch (UnknownHostException uhe) {
0426: // this shouldn't happen
0427: invalid = true;
0428: }
0429: }
0430: }
0431: }
0432: }
0433: }
0434:
0435: /**
0436: * Convert an action string to an integer actions mask.
0437: *
0438: * @param action the action string
0439: * @return the action mask
0440: */
0441: private static int getMask(String action) {
0442:
0443: if (action == null) {
0444: throw new NullPointerException("action can't be null");
0445: }
0446:
0447: if (action.equals("")) {
0448: throw new IllegalArgumentException("action can't be empty");
0449: }
0450:
0451: int mask = NONE;
0452:
0453: // Check against use of constants (used heavily within the JDK)
0454: if (action == SecurityConstants.SOCKET_RESOLVE_ACTION) {
0455: return RESOLVE;
0456: } else if (action == SecurityConstants.SOCKET_CONNECT_ACTION) {
0457: return CONNECT;
0458: } else if (action == SecurityConstants.SOCKET_LISTEN_ACTION) {
0459: return LISTEN;
0460: } else if (action == SecurityConstants.SOCKET_ACCEPT_ACTION) {
0461: return ACCEPT;
0462: } else if (action == SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION) {
0463: return CONNECT | ACCEPT;
0464: }
0465:
0466: char[] a = action.toCharArray();
0467:
0468: int i = a.length - 1;
0469: if (i < 0)
0470: return mask;
0471:
0472: while (i != -1) {
0473: char c;
0474:
0475: // skip whitespace
0476: while ((i != -1)
0477: && ((c = a[i]) == ' ' || c == '\r' || c == '\n'
0478: || c == '\f' || c == '\t'))
0479: i--;
0480:
0481: // check for the known strings
0482: int matchlen;
0483:
0484: if (i >= 6 && (a[i - 6] == 'c' || a[i - 6] == 'C')
0485: && (a[i - 5] == 'o' || a[i - 5] == 'O')
0486: && (a[i - 4] == 'n' || a[i - 4] == 'N')
0487: && (a[i - 3] == 'n' || a[i - 3] == 'N')
0488: && (a[i - 2] == 'e' || a[i - 2] == 'E')
0489: && (a[i - 1] == 'c' || a[i - 1] == 'C')
0490: && (a[i] == 't' || a[i] == 'T')) {
0491: matchlen = 7;
0492: mask |= CONNECT;
0493:
0494: } else if (i >= 6 && (a[i - 6] == 'r' || a[i - 6] == 'R')
0495: && (a[i - 5] == 'e' || a[i - 5] == 'E')
0496: && (a[i - 4] == 's' || a[i - 4] == 'S')
0497: && (a[i - 3] == 'o' || a[i - 3] == 'O')
0498: && (a[i - 2] == 'l' || a[i - 2] == 'L')
0499: && (a[i - 1] == 'v' || a[i - 1] == 'V')
0500: && (a[i] == 'e' || a[i] == 'E')) {
0501: matchlen = 7;
0502: mask |= RESOLVE;
0503:
0504: } else if (i >= 5 && (a[i - 5] == 'l' || a[i - 5] == 'L')
0505: && (a[i - 4] == 'i' || a[i - 4] == 'I')
0506: && (a[i - 3] == 's' || a[i - 3] == 'S')
0507: && (a[i - 2] == 't' || a[i - 2] == 'T')
0508: && (a[i - 1] == 'e' || a[i - 1] == 'E')
0509: && (a[i] == 'n' || a[i] == 'N')) {
0510: matchlen = 6;
0511: mask |= LISTEN;
0512:
0513: } else if (i >= 5 && (a[i - 5] == 'a' || a[i - 5] == 'A')
0514: && (a[i - 4] == 'c' || a[i - 4] == 'C')
0515: && (a[i - 3] == 'c' || a[i - 3] == 'C')
0516: && (a[i - 2] == 'e' || a[i - 2] == 'E')
0517: && (a[i - 1] == 'p' || a[i - 1] == 'P')
0518: && (a[i] == 't' || a[i] == 'T')) {
0519: matchlen = 6;
0520: mask |= ACCEPT;
0521:
0522: } else {
0523: // parse error
0524: throw new IllegalArgumentException(
0525: "invalid permission: " + action);
0526: }
0527:
0528: // make sure we didn't just match the tail of a word
0529: // like "ackbarfaccept". Also, skip to the comma.
0530: boolean seencomma = false;
0531: while (i >= matchlen && !seencomma) {
0532: switch (a[i - matchlen]) {
0533: case ',':
0534: seencomma = true;
0535: /*FALLTHROUGH*/
0536: case ' ':
0537: case '\r':
0538: case '\n':
0539: case '\f':
0540: case '\t':
0541: break;
0542: default:
0543: throw new IllegalArgumentException(
0544: "invalid permission: " + action);
0545: }
0546: i--;
0547: }
0548:
0549: // point i at the location of the comma minus one (or -1).
0550: i -= matchlen;
0551: }
0552:
0553: return mask;
0554: }
0555:
0556: /**
0557: * attempt to get the fully qualified domain name
0558: *
0559: */
0560: void getCanonName() throws UnknownHostException {
0561: if (cname != null || invalid)
0562: return;
0563:
0564: // attempt to get the canonical name
0565:
0566: try {
0567: // first get the IP addresses if we don't have them yet
0568: // this is because we need the IP address to then get
0569: // FQDN.
0570: if (addresses == null) {
0571: getIP();
0572: }
0573:
0574: // we have to do this check, otherwise we might not
0575: // get the fully qualified domain name
0576: if (init_with_ip) {
0577: cname = addresses[0].getHostName(false).toLowerCase();
0578: } else {
0579: cname = InetAddress.getByName(
0580: addresses[0].getHostAddress()).getHostName(
0581: false).toLowerCase();
0582: }
0583: } catch (UnknownHostException uhe) {
0584: invalid = true;
0585: throw uhe;
0586: }
0587: }
0588:
0589: /**
0590: * get IP addresses. Sets invalid to true if we can't get them.
0591: *
0592: */
0593: void getIP() throws UnknownHostException {
0594: if (addresses != null || wildcard || invalid)
0595: return;
0596:
0597: try {
0598: // now get all the IP addresses
0599: String host;
0600: if (getName().charAt(0) == '[') {
0601: // Literal IPv6 address
0602: host = getName().substring(1, getName().indexOf(']'));
0603: } else {
0604: int i = getName().indexOf(":");
0605: if (i == -1)
0606: host = getName();
0607: else {
0608: host = getName().substring(0, i);
0609: }
0610: }
0611:
0612: addresses = new InetAddress[] { InetAddress.getAllByName0(
0613: host, false)[0] };
0614:
0615: } catch (UnknownHostException uhe) {
0616: invalid = true;
0617: throw uhe;
0618: } catch (IndexOutOfBoundsException iobe) {
0619: invalid = true;
0620: throw new UnknownHostException(getName());
0621: }
0622: }
0623:
0624: /**
0625: * Checks if this socket permission object "implies" the
0626: * specified permission.
0627: * <P>
0628: * More specifically, this method first ensures that all of the following
0629: * are true (and returns false if any of them are not):<p>
0630: * <ul>
0631: * <li> <i>p</i> is an instanceof SocketPermission,<p>
0632: * <li> <i>p</i>'s actions are a proper subset of this
0633: * object's actions, and<p>
0634: * <li> <i>p</i>'s port range is included in this port range. Note:
0635: * port range is ignored when p only contains the action, 'resolve'.<p>
0636: * </ul>
0637: *
0638: * Then <code>implies</code> checks each of the following, in order,
0639: * and for each returns true if the stated condition is true:<p>
0640: * <ul>
0641: * <li> If this object was initialized with a single IP address and one of <i>p</i>'s
0642: * IP addresses is equal to this object's IP address.<p>
0643: * <li>If this object is a wildcard domain (such as *.sun.com), and
0644: * <i>p</i>'s canonical name (the name without any preceding *)
0645: * ends with this object's canonical host name. For example, *.sun.com
0646: * implies *.eng.sun.com..<p>
0647: * <li>If this object was not initialized with a single IP address, and one of this
0648: * object's IP addresses equals one of <i>p</i>'s IP addresses.<p>
0649: * <li>If this canonical name equals <i>p</i>'s canonical name.<p>
0650: * </ul>
0651: *
0652: * If none of the above are true, <code>implies</code> returns false.
0653: * @param p the permission to check against.
0654: *
0655: * @return true if the specified permission is implied by this object,
0656: * false if not.
0657: */
0658:
0659: public boolean implies(Permission p) {
0660: int i, j;
0661:
0662: if (!(p instanceof SocketPermission))
0663: return false;
0664:
0665: SocketPermission that = (SocketPermission) p;
0666:
0667: return ((this .mask & that.mask) == that.mask)
0668: && impliesIgnoreMask(that);
0669: }
0670:
0671: /**
0672: * Checks if the incoming Permission's action are a proper subset of
0673: * the this object's actions.
0674: * <P>
0675: * Check, in the following order:
0676: * <ul>
0677: * <li> Checks that "p" is an instanceof a SocketPermission
0678: * <li> Checks that "p"'s actions are a proper subset of the
0679: * current object's actions.
0680: * <li> Checks that "p"'s port range is included in this port range
0681: * <li> If this object was initialized with an IP address, checks that
0682: * one of "p"'s IP addresses is equal to this object's IP address.
0683: * <li> If either object is a wildcard domain (i.e., "*.sun.com"),
0684: * attempt to match based on the wildcard.
0685: * <li> If this object was not initialized with an IP address, attempt
0686: * to find a match based on the IP addresses in both objects.
0687: * <li> Attempt to match on the canonical hostnames of both objects.
0688: * </ul>
0689: * @param p the incoming permission request
0690: *
0691: * @return true if "permission" is a proper subset of the current object,
0692: * false if not.
0693: */
0694:
0695: boolean impliesIgnoreMask(SocketPermission that) {
0696: int i, j;
0697:
0698: if ((that.mask & RESOLVE) != that.mask) {
0699: // check port range
0700: if ((that.portrange[0] < this .portrange[0])
0701: || (that.portrange[1] > this .portrange[1])) {
0702: return false;
0703: }
0704: }
0705:
0706: // allow a "*" wildcard to always match anything
0707: if (this .wildcard && "".equals(this .cname))
0708: return true;
0709:
0710: // return if either one of these NetPerm objects are invalid...
0711: if (this .invalid || that.invalid) {
0712: return (trustProxy ? inProxyWeTrust(that) : false);
0713: }
0714:
0715: try {
0716: if (this .init_with_ip) { // we only check IP addresses
0717: if (that.wildcard)
0718: return false;
0719:
0720: if (that.init_with_ip) {
0721: return (this .addresses[0].equals(that.addresses[0]));
0722: } else {
0723: if (that.addresses == null) {
0724: that.getIP();
0725: }
0726: for (i = 0; i < that.addresses.length; i++) {
0727: if (this .addresses[0].equals(that.addresses[i]))
0728: return true;
0729: }
0730: }
0731: // since "this" was initialized with an IP address, we
0732: // don't check any other cases
0733: return false;
0734: }
0735:
0736: // check and see if we have any wildcards...
0737: if (this .wildcard || that.wildcard) {
0738: // if they are both wildcards, return true iff
0739: // that's cname ends with this cname (i.e., *.sun.com
0740: // implies *.eng.sun.com)
0741: if (this .wildcard && that.wildcard)
0742: return (that.cname.endsWith(this .cname));
0743:
0744: // a non-wildcard can't imply a wildcard
0745: if (that.wildcard)
0746: return false;
0747:
0748: // this is a wildcard, lets see if that's cname ends with
0749: // it...
0750: if (that.cname == null) {
0751: that.getCanonName();
0752: }
0753: return (that.cname.endsWith(this .cname));
0754: }
0755:
0756: // comapare IP addresses
0757: if (this .addresses == null) {
0758: this .getIP();
0759: }
0760:
0761: if (that.addresses == null) {
0762: that.getIP();
0763: }
0764:
0765: for (j = 0; j < this .addresses.length; j++) {
0766: for (i = 0; i < that.addresses.length; i++) {
0767: if (this .addresses[j].equals(that.addresses[i]))
0768: return true;
0769: }
0770: }
0771:
0772: // If all else fails, compare hostnames?
0773: // Do we really want this?
0774: if (this .cname == null) {
0775: this .getCanonName();
0776: }
0777:
0778: if (that.cname == null) {
0779: that.getCanonName();
0780: }
0781:
0782: return (this .cname.equalsIgnoreCase(that.cname));
0783:
0784: } catch (UnknownHostException uhe) {
0785: if (trustProxy)
0786: return inProxyWeTrust(that);
0787: }
0788:
0789: // make sure the first thing that is done here is to return
0790: // false. If not, uncomment the return false in the above catch.
0791:
0792: return false;
0793: }
0794:
0795: private boolean inProxyWeTrust(SocketPermission that) {
0796: // if we trust the proxy, we see if the original names/IPs passed
0797: // in were equal.
0798:
0799: String this Host = hostname;
0800: String thatHost = that.hostname;
0801:
0802: if (this Host == null)
0803: return false;
0804: else
0805: return this Host.equalsIgnoreCase(thatHost);
0806:
0807: }
0808:
0809: /**
0810: * Checks two SocketPermission objects for equality.
0811: * <P>
0812: * @param obj the object to test for equality with this object.
0813: *
0814: * @return true if <i>obj</i> is a SocketPermission, and has the
0815: * same hostname, port range, and actions as this
0816: * SocketPermission object. However, port range will be ignored
0817: * in the comparison if <i>obj</i> only contains the action, 'resolve'.
0818: */
0819: public boolean equals(Object obj) {
0820: if (obj == this )
0821: return true;
0822:
0823: if (!(obj instanceof SocketPermission))
0824: return false;
0825:
0826: SocketPermission that = (SocketPermission) obj;
0827:
0828: //this is (overly?) complex!!!
0829:
0830: // check the mask first
0831: if (this .mask != that.mask)
0832: return false;
0833:
0834: if ((that.mask & RESOLVE) != that.mask) {
0835: // now check the port range...
0836: if ((this .portrange[0] != that.portrange[0])
0837: || (this .portrange[1] != that.portrange[1])) {
0838: return false;
0839: }
0840: }
0841:
0842: // short cut. This catches:
0843: // "crypto" equal to "crypto", or
0844: // "1.2.3.4" equal to "1.2.3.4.", or
0845: // "*.edu" equal to "*.edu", but it
0846: // does not catch "crypto" equal to
0847: // "crypto.eng.sun.com".
0848:
0849: if (this .getName().equalsIgnoreCase(that.getName())) {
0850: return true;
0851: }
0852:
0853: // we now attempt to get the Canonical (FQDN) name and
0854: // compare that. If this fails, about all we can do is return
0855: // false.
0856:
0857: try {
0858: this .getCanonName();
0859: that.getCanonName();
0860: } catch (UnknownHostException uhe) {
0861: return false;
0862: }
0863:
0864: if (this .invalid || that.invalid)
0865: return false;
0866:
0867: if (this .cname != null) {
0868: return this .cname.equalsIgnoreCase(that.cname);
0869: }
0870:
0871: return false;
0872: }
0873:
0874: /**
0875: * Returns the hash code value for this object.
0876: *
0877: * @return a hash code value for this object.
0878: */
0879:
0880: public int hashCode() {
0881: /*
0882: * If this SocketPermission was initialized with an IP address
0883: * or a wildcard, use getName().hashCode(), otherwise use
0884: * the hashCode() of the host name returned from
0885: * java.net.InetAddress.getHostName method.
0886: */
0887:
0888: if (init_with_ip || wildcard) {
0889: return this .getName().hashCode();
0890: }
0891:
0892: try {
0893: getCanonName();
0894: } catch (UnknownHostException uhe) {
0895:
0896: }
0897:
0898: if (invalid || cname == null)
0899: return this .getName().hashCode();
0900: else
0901: return this .cname.hashCode();
0902: }
0903:
0904: /**
0905: * Return the current action mask.
0906: *
0907: * @return the actions mask.
0908: */
0909:
0910: int getMask() {
0911: return mask;
0912: }
0913:
0914: /**
0915: * Returns the "canonical string representation" of the actions in the
0916: * specified mask.
0917: * Always returns present actions in the following order:
0918: * connect, listen, accept, resolve.
0919: *
0920: * @param mask a specific integer action mask to translate into a string
0921: * @return the canonical string representation of the actions
0922: */
0923: private static String getActions(int mask) {
0924: StringBuffer sb = new StringBuffer();
0925: boolean comma = false;
0926:
0927: if ((mask & CONNECT) == CONNECT) {
0928: comma = true;
0929: sb.append("connect");
0930: }
0931:
0932: if ((mask & LISTEN) == LISTEN) {
0933: if (comma)
0934: sb.append(',');
0935: else
0936: comma = true;
0937: sb.append("listen");
0938: }
0939:
0940: if ((mask & ACCEPT) == ACCEPT) {
0941: if (comma)
0942: sb.append(',');
0943: else
0944: comma = true;
0945: sb.append("accept");
0946: }
0947:
0948: if ((mask & RESOLVE) == RESOLVE) {
0949: if (comma)
0950: sb.append(',');
0951: else
0952: comma = true;
0953: sb.append("resolve");
0954: }
0955:
0956: return sb.toString();
0957: }
0958:
0959: /**
0960: * Returns the canonical string representation of the actions.
0961: * Always returns present actions in the following order:
0962: * connect, listen, accept, resolve.
0963: *
0964: * @return the canonical string representation of the actions.
0965: */
0966: public String getActions() {
0967: if (actions == null)
0968: actions = getActions(this .mask);
0969:
0970: return actions;
0971: }
0972:
0973: /**
0974: * Returns a new PermissionCollection object for storing SocketPermission
0975: * objects.
0976: * <p>
0977: * SocketPermission objects must be stored in a manner that allows them
0978: * to be inserted into the collection in any order, but that also enables the
0979: * PermissionCollection <code>implies</code>
0980: * method to be implemented in an efficient (and consistent) manner.
0981: *
0982: * @return a new PermissionCollection object suitable for storing SocketPermissions.
0983: */
0984:
0985: public PermissionCollection newPermissionCollection() {
0986: return new SocketPermissionCollection();
0987: }
0988:
0989: /**
0990: * WriteObject is called to save the state of the SocketPermission
0991: * to a stream. The actions are serialized, and the superclass
0992: * takes care of the name.
0993: */
0994: private synchronized void writeObject(java.io.ObjectOutputStream s)
0995: throws IOException {
0996: // Write out the actions. The superclass takes care of the name
0997: // call getActions to make sure actions field is initialized
0998: if (actions == null)
0999: getActions();
1000: s.defaultWriteObject();
1001: }
1002:
1003: /**
1004: * readObject is called to restore the state of the SocketPermission from
1005: * a stream.
1006: */
1007: private synchronized void readObject(java.io.ObjectInputStream s)
1008: throws IOException, ClassNotFoundException {
1009: // Read in the action, then initialize the rest
1010: s.defaultReadObject();
1011: init(getName(), getMask(actions));
1012: }
1013:
1014: /*
1015: public String toString()
1016: {
1017: StringBuffer s = new StringBuffer(super.toString() + "\n" +
1018: "cname = " + cname + "\n" +
1019: "wildcard = " + wildcard + "\n" +
1020: "invalid = " + invalid + "\n" +
1021: "portrange = " + portrange[0] + "," + portrange[1] + "\n");
1022: if (addresses != null) for (int i=0; i<addresses.length; i++) {
1023: s.append( addresses[i].getHostAddress());
1024: s.append("\n");
1025: } else {
1026: s.append("(no addresses)\n");
1027: }
1028:
1029: return s.toString();
1030: }
1031:
1032: public static void main(String args[]) throws Exception {
1033: SocketPermission this_ = new SocketPermission(args[0], "connect");
1034: SocketPermission that_ = new SocketPermission(args[1], "connect");
1035: System.out.println("-----\n");
1036: System.out.println("this.implies(that) = " + this_.implies(that_));
1037: System.out.println("-----\n");
1038: System.out.println("this = "+this_);
1039: System.out.println("-----\n");
1040: System.out.println("that = "+that_);
1041: System.out.println("-----\n");
1042:
1043: SocketPermissionCollection nps = new SocketPermissionCollection();
1044: nps.add(this_);
1045: nps.add(new SocketPermission("www-leland.stanford.edu","connect"));
1046: nps.add(new SocketPermission("www-sun.com","connect"));
1047: System.out.println("nps.implies(that) = " + nps.implies(that_));
1048: System.out.println("-----\n");
1049: }
1050: */
1051: }
1052:
1053: /**
1054:
1055: if (init'd with IP, key is IP as string)
1056: if wildcard, its the wild card
1057: else its the cname?
1058:
1059: *
1060: * @see java.security.Permission
1061: * @see java.security.Permissions
1062: * @see java.security.PermissionCollection
1063: *
1064: * @version 1.41 10/27/00
1065: *
1066: * @author Roland Schemers
1067: *
1068: * @serial include
1069: */
1070:
1071: final class SocketPermissionCollection extends PermissionCollection
1072: implements Serializable {
1073: // Not serialized; see serialization section at end of class
1074: private transient List perms;
1075:
1076: /**
1077: * Create an empty SocketPermissions object.
1078: *
1079: */
1080:
1081: public SocketPermissionCollection() {
1082: perms = new ArrayList();
1083: }
1084:
1085: /**
1086: * Adds a permission to the SocketPermissions. The key for the hash is
1087: * the name in the case of wildcards, or all the IP addresses.
1088: *
1089: * @param permission the Permission object to add.
1090: *
1091: * @exception IllegalArgumentException - if the permission is not a
1092: * SocketPermission
1093: *
1094: * @exception SecurityException - if this SocketPermissionCollection object
1095: * has been marked readonly
1096: */
1097:
1098: public void add(Permission permission) {
1099: if (!(permission instanceof SocketPermission))
1100: throw new IllegalArgumentException("invalid permission: "
1101: + permission);
1102: if (isReadOnly())
1103: throw new SecurityException(
1104: "attempt to add a Permission to a readonly PermissionCollection");
1105:
1106: // No need to synchronize because all adds are done sequentially
1107: // before any implies() calls
1108:
1109: // optimization to ensure perms most likely to be tested
1110: // show up early (4301064)
1111: perms.add(0, permission);
1112: }
1113:
1114: /**
1115: * Check and see if this collection of permissions implies the permissions
1116: * expressed in "permission".
1117: *
1118: * @param p the Permission object to compare
1119: *
1120: * @return true if "permission" is a proper subset of a permission in
1121: * the collection, false if not.
1122: */
1123:
1124: public boolean implies(Permission permission) {
1125: if (!(permission instanceof SocketPermission))
1126: return false;
1127:
1128: SocketPermission np = (SocketPermission) permission;
1129:
1130: int desired = np.getMask();
1131: int effective = 0;
1132: int needed = desired;
1133:
1134: int len = perms.size();
1135: //System.out.println("implies "+np);
1136: for (int i = 0; i < len; i++) {
1137: SocketPermission x = (SocketPermission) perms.get(i);
1138: //System.out.println(" trying "+x);
1139: if (((needed & x.getMask()) != 0)
1140: && x.impliesIgnoreMask(np)) {
1141: effective |= x.getMask();
1142: if ((effective & desired) == desired)
1143: return true;
1144: needed = (desired ^ effective);
1145: }
1146: }
1147: return false;
1148: }
1149:
1150: /**
1151: * Returns an enumeration of all the SocketPermission objects in the
1152: * container.
1153: *
1154: * @return an enumeration of all the SocketPermission objects.
1155: */
1156:
1157: public Enumeration elements() {
1158: // Convert Iterator into Enumeration
1159: return Collections.enumeration(perms);
1160: }
1161:
1162: private static final long serialVersionUID = 2787186408602843674L;
1163:
1164: // Need to maintain serialization interoperability with earlier releases,
1165: // which had the serializable field:
1166:
1167: //
1168: // The SocketPermissions for this set.
1169: // @serial
1170: //
1171: // private Vector permissions;
1172:
1173: /**
1174: * @serialField permissions java.util.Vector
1175: * A list of the SocketPermissions for this set.
1176: */
1177: private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField(
1178: "permissions", Vector.class), };
1179:
1180: /**
1181: * @serialData "permissions" field (a Vector containing the SocketPermissions).
1182: */
1183: /*
1184: * Writes the contents of the perms field out as a Vector for
1185: * serialization compatibility with earlier releases.
1186: */
1187: private void writeObject(ObjectOutputStream out) throws IOException {
1188: // Don't call out.defaultWriteObject()
1189:
1190: // Write out Vector
1191: Vector permissions = new Vector(perms.size());
1192: permissions.addAll(perms);
1193:
1194: ObjectOutputStream.PutField pfields = out.putFields();
1195: pfields.put("permissions", permissions);
1196: out.writeFields();
1197: }
1198:
1199: /*
1200: * Reads in a Vector of SocketPermissions and saves them in the perms field.
1201: */
1202: private void readObject(ObjectInputStream in) throws IOException,
1203: ClassNotFoundException {
1204: // Don't call in.defaultReadObject()
1205:
1206: // Read in serialized fields
1207: ObjectInputStream.GetField gfields = in.readFields();
1208:
1209: // Get the one we want
1210: Vector permissions = (Vector) gfields.get("permissions", null);
1211: perms = new ArrayList(permissions.size());
1212: perms.addAll(permissions);
1213: }
1214: }
|