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