1. /*
  2. * @(#)SocketPermission.java 1.59 04/02/12
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.net;
  8. import java.util.Enumeration;
  9. import java.util.Vector;
  10. import java.util.List;
  11. import java.util.ArrayList;
  12. import java.util.Collections;
  13. import java.util.StringTokenizer;
  14. import java.net.InetAddress;
  15. import java.security.Permission;
  16. import java.security.PermissionCollection;
  17. import java.io.Serializable;
  18. import java.io.ObjectStreamField;
  19. import java.io.ObjectOutputStream;
  20. import java.io.ObjectInputStream;
  21. import java.io.IOException;
  22. import sun.net.util.IPAddressUtil;
  23. import sun.security.util.SecurityConstants;
  24. /**
  25. * This class represents access to a network via sockets.
  26. * A SocketPermission consists of a
  27. * host specification and a set of "actions" specifying ways to
  28. * connect to that host. The host is specified as
  29. * <pre>
  30. * host = (hostname | IPv4address | iPv6reference) [:portrange]
  31. * portrange = portnumber | -portnumber | portnumber-[portnumber]
  32. * </pre>
  33. * The host is expressed as a DNS name, as a numerical IP address,
  34. * or as "localhost" (for the local machine).
  35. * The wildcard "*" may be included once in a DNS name host
  36. * specification. If it is included, it must be in the leftmost
  37. * position, as in "*.sun.com".
  38. * <p>
  39. * The format of the IPv6reference should follow that specified in <a
  40. * href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC 2732: Format
  41. * for Literal IPv6 Addresses in URLs</i></a>:
  42. * <pre>
  43. * ipv6reference = "[" IPv6address "]"
  44. *</pre>
  45. * For example, you can construct a SocketPermission instance
  46. * as the following:
  47. * <pre>
  48. * String hostAddress = inetaddress.getHostAddress();
  49. * if (inetaddress instanceof Inet6Address) {
  50. * sp = new SocketPermission("[" + hostAddress + "]:" + port, action);
  51. * } else {
  52. * sp = new SocketPermission(hostAddress + ":" + port, action);
  53. * }
  54. * </pre>
  55. * or
  56. * <pre>
  57. * String host = url.getHost();
  58. * sp = new SocketPermission(host + ":" + port, action);
  59. * </pre>
  60. * <p>
  61. * The <A HREF="Inet6Address.html#lform">full uncompressed form</A> of
  62. * an IPv6 literal address is also valid.
  63. * <p>
  64. * The port or portrange is optional. A port specification of the
  65. * form "N-", where <i>N</i> is a port number, signifies all ports
  66. * numbered <i>N</i> and above, while a specification of the
  67. * form "-N" indicates all ports numbered <i>N</i> and below.
  68. * <p>
  69. * The possible ways to connect to the host are
  70. * <pre>
  71. * accept
  72. * connect
  73. * listen
  74. * resolve
  75. * </pre>
  76. * The "listen" action is only meaningful when used with "localhost".
  77. * The "resolve" action is implied when any of the other actions are present.
  78. * The action "resolve" refers to host/ip name service lookups.
  79. *
  80. * <p>As an example of the creation and meaning of SocketPermissions,
  81. * note that if the following permission:
  82. *
  83. * <pre>
  84. * p1 = new SocketPermission("puffin.eng.sun.com:7777", "connect,accept");
  85. * </pre>
  86. *
  87. * is granted to some code, it allows that code to connect to port 7777 on
  88. * <code>puffin.eng.sun.com</code>, and to accept connections on that port.
  89. *
  90. * <p>Similarly, if the following permission:
  91. *
  92. * <pre>
  93. * p1 = new SocketPermission("puffin.eng.sun.com:7777", "connect,accept");
  94. * p2 = new SocketPermission("localhost:1024-", "accept,connect,listen");
  95. * </pre>
  96. *
  97. * is granted to some code, it allows that code to
  98. * accept connections on, connect to, or listen on any port between
  99. * 1024 and 65535 on the local host.
  100. *
  101. * <p>Note: Granting code permission to accept or make connections to remote
  102. * hosts may be dangerous because malevolent code can then more easily
  103. * transfer and share confidential data among parties who may not
  104. * otherwise have access to the data.
  105. *
  106. * @see java.security.Permissions
  107. * @see SocketPermission
  108. *
  109. * @version 1.59 04/02/12
  110. *
  111. * @author Marianne Mueller
  112. * @author Roland Schemers
  113. *
  114. * @serial exclude
  115. */
  116. public final class SocketPermission extends Permission
  117. implements java.io.Serializable
  118. {
  119. private static final long serialVersionUID = -7204263841984476862L;
  120. /**
  121. * Connect to host:port
  122. */
  123. private final static int CONNECT = 0x1;
  124. /**
  125. * Listen on host:port
  126. */
  127. private final static int LISTEN = 0x2;
  128. /**
  129. * Accept a connection from host:port
  130. */
  131. private final static int ACCEPT = 0x4;
  132. /**
  133. * Resolve DNS queries
  134. */
  135. private final static int RESOLVE = 0x8;
  136. /**
  137. * No actions
  138. */
  139. private final static int NONE = 0x0;
  140. /**
  141. * All actions
  142. */
  143. private final static int ALL = CONNECT|LISTEN|ACCEPT|RESOLVE;
  144. // various port constants
  145. private static final int PORT_MIN = 0;
  146. private static final int PORT_MAX = 65535;
  147. private static final int PRIV_PORT_MAX = 1023;
  148. // the actions mask
  149. private transient int mask;
  150. /**
  151. * the actions string.
  152. *
  153. * @serial
  154. */
  155. private String actions; // Left null as long as possible, then
  156. // created and re-used in the getAction function.
  157. // hostname part as it is passed
  158. private transient String hostname;
  159. // the canonical name of the host
  160. // in the case of "*.foo.com", cname is ".foo.com".
  161. private transient String cname;
  162. // all the IP addresses of the host
  163. private transient InetAddress[] addresses;
  164. // true if the hostname is a wildcard (e.g. "*.sun.com")
  165. private transient boolean wildcard;
  166. // true if we were initialized with a single numeric IP address
  167. private transient boolean init_with_ip;
  168. // true if this SocketPermission represents an invalid/unknown host
  169. // used for implies when the delayed lookup has already failed
  170. private transient boolean invalid;
  171. // port range on host
  172. private transient int[] portrange;
  173. // true if the trustProxy system property is set
  174. private static boolean trustProxy;
  175. static {
  176. Boolean tmp = (Boolean) java.security.AccessController.doPrivileged(
  177. new sun.security.action.GetBooleanAction("trustProxy"));
  178. trustProxy = tmp.booleanValue();
  179. }
  180. /**
  181. * Creates a new SocketPermission object with the specified actions.
  182. * The host is expressed as a DNS name, or as a numerical IP address.
  183. * Optionally, a port or a portrange may be supplied (separated
  184. * from the DNS name or IP address by a colon).
  185. * <p>
  186. * To specify the local machine, use "localhost" as the <i>host</i>.
  187. * Also note: An empty <i>host</i> String ("") is equivalent to "localhost".
  188. * <p>
  189. * The <i>actions</i> parameter contains a comma-separated list of the
  190. * actions granted for the specified host (and port(s)). Possible actions are
  191. * "connect", "listen", "accept", "resolve", or
  192. * any combination of those. "resolve" is automatically added
  193. * when any of the other three are specified.
  194. * <p>
  195. * Examples of SocketPermission instantiation are the following:
  196. * <pre>
  197. * nr = new SocketPermission("www.catalog.com", "connect");
  198. * nr = new SocketPermission("www.sun.com:80", "connect");
  199. * nr = new SocketPermission("*.sun.com", "connect");
  200. * nr = new SocketPermission("*.edu", "resolve");
  201. * nr = new SocketPermission("204.160.241.0", "connect");
  202. * nr = new SocketPermission("localhost:1024-65535", "listen");
  203. * nr = new SocketPermission("204.160.241.0:1024-65535", "connect");
  204. * </pre>
  205. *
  206. * @param host the hostname or IPaddress of the computer, optionally
  207. * including a colon followed by a port or port range.
  208. * @param action the action string.
  209. */
  210. public SocketPermission(String host, String action) {
  211. super(getHost(host));
  212. // name initialized to getHost(host); NPE detected in getHost()
  213. init(getName(), getMask(action));
  214. }
  215. SocketPermission(String host, int mask) {
  216. super(getHost(host));
  217. // name initialized to getHost(host); NPE detected in getHost()
  218. init(getName(), mask);
  219. }
  220. private static String getHost(String host)
  221. {
  222. if (host.equals("")) {
  223. return "localhost";
  224. } else {
  225. /* IPv6 literal address used in this context should follow
  226. * the format specified in RFC 2732;
  227. * if not, we try to solve the unambiguous case
  228. */
  229. int ind;
  230. if (host.charAt(0) != '[') {
  231. if ((ind = host.indexOf(':')) != host.lastIndexOf(':')) {
  232. /* More than one ":", meaning IPv6 address is not
  233. * in RFC 2732 format;
  234. * We will rectify user errors for all unambiguious cases
  235. */
  236. StringTokenizer st = new StringTokenizer(host, ":");
  237. int tokens = st.countTokens();
  238. if (tokens == 9) {
  239. // IPv6 address followed by port
  240. ind = host.lastIndexOf(':');
  241. host = "[" + host.substring(0, ind) + "]" +
  242. host.substring(ind);
  243. } else if (tokens == 8 && host.indexOf("::") == -1) {
  244. // IPv6 address only, not followed by port
  245. host = "[" + host + "]";
  246. } else {
  247. // could be ambiguous
  248. throw new IllegalArgumentException("Ambiguous"+
  249. " hostport part");
  250. }
  251. }
  252. }
  253. return host;
  254. }
  255. }
  256. private int[] parsePort(String port)
  257. throws Exception
  258. {
  259. if (port == null || port.equals("") || port.equals("*")) {
  260. return new int[] {PORT_MIN, PORT_MAX};
  261. }
  262. int dash = port.indexOf('-');
  263. if (dash == -1) {
  264. int p = Integer.parseInt(port);
  265. return new int[] {p, p};
  266. } else {
  267. String low = port.substring(0, dash);
  268. String high = port.substring(dash+1);
  269. int l,h;
  270. if (low.equals("")) {
  271. l = PORT_MIN;
  272. } else {
  273. l = Integer.parseInt(low);
  274. }
  275. if (high.equals("")) {
  276. h = PORT_MAX;
  277. } else {
  278. h = Integer.parseInt(high);
  279. }
  280. if (l < 0 || h < 0 || h<l)
  281. throw new IllegalArgumentException("invalid port range");
  282. return new int[] {l, h};
  283. }
  284. }
  285. /**
  286. * Initialize the SocketPermission object. We don't do any DNS lookups
  287. * as this point, instead we hold off until the implies method is
  288. * called.
  289. */
  290. private void init(String host, int mask) {
  291. // Set the integer mask that represents the actions
  292. if ((mask & ALL) != mask)
  293. throw new IllegalArgumentException("invalid actions mask");
  294. // always OR in RESOLVE if we allow any of the others
  295. this.mask = mask | RESOLVE;
  296. // Parse the host name. A name has up to three components, the
  297. // hostname, a port number, or two numbers representing a port
  298. // range. "www.sun.com:8080-9090" is a valid host name.
  299. // With IPv6 an address can be 2010:836B:4179::836B:4179
  300. // An IPv6 address needs to be enclose in []
  301. // For ex: [2010:836B:4179::836B:4179]:8080-9090
  302. // Refer to RFC 2732 for more information.
  303. int rb = 0 ;
  304. int start = 0, end = 0;
  305. int sep = -1;
  306. String hostport = host;
  307. if (host.charAt(0) == '[') {
  308. start = 1;
  309. rb = host.indexOf(']');
  310. if (rb != -1) {
  311. host = host.substring(start, rb);
  312. } else {
  313. throw new
  314. IllegalArgumentException("invalid host/port: "+host);
  315. }
  316. sep = hostport.indexOf(':', rb+1);
  317. } else {
  318. start = 0;
  319. sep = host.indexOf(':', rb);
  320. end = sep;
  321. if (sep != -1) {
  322. host = host.substring(start, end);
  323. }
  324. }
  325. if (sep != -1) {
  326. String port = hostport.substring(sep+1);
  327. try {
  328. portrange = parsePort(port);
  329. } catch (Exception e) {
  330. throw new
  331. IllegalArgumentException("invalid port range: "+port);
  332. }
  333. } else {
  334. portrange = new int[] { PORT_MIN, PORT_MAX };
  335. }
  336. hostname = host;
  337. // is this a domain wildcard specification
  338. if (host.lastIndexOf('*') > 0) {
  339. throw new
  340. IllegalArgumentException("invalid host wildcard specification");
  341. } else if (host.startsWith("*")) {
  342. wildcard = true;
  343. if (host.equals("*")) {
  344. cname = "";
  345. } else if (host.startsWith("*.")) {
  346. cname = host.substring(1).toLowerCase();
  347. } else {
  348. throw new
  349. IllegalArgumentException("invalid host wildcard specification");
  350. }
  351. return;
  352. } else {
  353. if (host.length() > 0) {
  354. // see if we are being initialized with an IP address.
  355. char ch = host.charAt(0);
  356. if (ch == ':' || Character.digit(ch, 16) != -1) {
  357. byte ip[] = IPAddressUtil.textToNumericFormatV4(host);
  358. if (ip == null) {
  359. ip = IPAddressUtil.textToNumericFormatV6(host);
  360. }
  361. if (ip != null) {
  362. try {
  363. addresses =
  364. new InetAddress[]
  365. {InetAddress.getByAddress(ip) };
  366. init_with_ip = true;
  367. } catch (UnknownHostException uhe) {
  368. // this shouldn't happen
  369. invalid = true;
  370. }
  371. }
  372. }
  373. }
  374. }
  375. }
  376. /**
  377. * Convert an action string to an integer actions mask.
  378. *
  379. * @param action the action string
  380. * @return the action mask
  381. */
  382. private static int getMask(String action) {
  383. if (action == null) {
  384. throw new NullPointerException("action can't be null");
  385. }
  386. if (action.equals("")) {
  387. throw new IllegalArgumentException("action can't be empty");
  388. }
  389. int mask = NONE;
  390. // Check against use of constants (used heavily within the JDK)
  391. if (action == SecurityConstants.SOCKET_RESOLVE_ACTION) {
  392. return RESOLVE;
  393. } else if (action == SecurityConstants.SOCKET_CONNECT_ACTION) {
  394. return CONNECT;
  395. } else if (action == SecurityConstants.SOCKET_LISTEN_ACTION) {
  396. return LISTEN;
  397. } else if (action == SecurityConstants.SOCKET_ACCEPT_ACTION) {
  398. return ACCEPT;
  399. } else if (action == SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION) {
  400. return CONNECT|ACCEPT;
  401. }
  402. char[] a = action.toCharArray();
  403. int i = a.length - 1;
  404. if (i < 0)
  405. return mask;
  406. while (i != -1) {
  407. char c;
  408. // skip whitespace
  409. while ((i!=-1) && ((c = a[i]) == ' ' ||
  410. c == '\r' ||
  411. c == '\n' ||
  412. c == '\f' ||
  413. c == '\t'))
  414. i--;
  415. // check for the known strings
  416. int matchlen;
  417. if (i >= 6 && (a[i-6] == 'c' || a[i-6] == 'C') &&
  418. (a[i-5] == 'o' || a[i-5] == 'O') &&
  419. (a[i-4] == 'n' || a[i-4] == 'N') &&
  420. (a[i-3] == 'n' || a[i-3] == 'N') &&
  421. (a[i-2] == 'e' || a[i-2] == 'E') &&
  422. (a[i-1] == 'c' || a[i-1] == 'C') &&
  423. (a[i] == 't' || a[i] == 'T'))
  424. {
  425. matchlen = 7;
  426. mask |= CONNECT;
  427. } else if (i >= 6 && (a[i-6] == 'r' || a[i-6] == 'R') &&
  428. (a[i-5] == 'e' || a[i-5] == 'E') &&
  429. (a[i-4] == 's' || a[i-4] == 'S') &&
  430. (a[i-3] == 'o' || a[i-3] == 'O') &&
  431. (a[i-2] == 'l' || a[i-2] == 'L') &&
  432. (a[i-1] == 'v' || a[i-1] == 'V') &&
  433. (a[i] == 'e' || a[i] == 'E'))
  434. {
  435. matchlen = 7;
  436. mask |= RESOLVE;
  437. } else if (i >= 5 && (a[i-5] == 'l' || a[i-5] == 'L') &&
  438. (a[i-4] == 'i' || a[i-4] == 'I') &&
  439. (a[i-3] == 's' || a[i-3] == 'S') &&
  440. (a[i-2] == 't' || a[i-2] == 'T') &&
  441. (a[i-1] == 'e' || a[i-1] == 'E') &&
  442. (a[i] == 'n' || a[i] == 'N'))
  443. {
  444. matchlen = 6;
  445. mask |= LISTEN;
  446. } else if (i >= 5 && (a[i-5] == 'a' || a[i-5] == 'A') &&
  447. (a[i-4] == 'c' || a[i-4] == 'C') &&
  448. (a[i-3] == 'c' || a[i-3] == 'C') &&
  449. (a[i-2] == 'e' || a[i-2] == 'E') &&
  450. (a[i-1] == 'p' || a[i-1] == 'P') &&
  451. (a[i] == 't' || a[i] == 'T'))
  452. {
  453. matchlen = 6;
  454. mask |= ACCEPT;
  455. } else {
  456. // parse error
  457. throw new IllegalArgumentException(
  458. "invalid permission: " + action);
  459. }
  460. // make sure we didn't just match the tail of a word
  461. // like "ackbarfaccept". Also, skip to the comma.
  462. boolean seencomma = false;
  463. while (i >= matchlen && !seencomma) {
  464. switch(a[i-matchlen]) {
  465. case ',':
  466. seencomma = true;
  467. /*FALLTHROUGH*/
  468. case ' ': case '\r': case '\n':
  469. case '\f': case '\t':
  470. break;
  471. default:
  472. throw new IllegalArgumentException(
  473. "invalid permission: " + action);
  474. }
  475. i--;
  476. }
  477. // point i at the location of the comma minus one (or -1).
  478. i -= matchlen;
  479. }
  480. return mask;
  481. }
  482. /**
  483. * attempt to get the fully qualified domain name
  484. *
  485. */
  486. void getCanonName()
  487. throws UnknownHostException
  488. {
  489. if (cname != null || invalid) return;
  490. // attempt to get the canonical name
  491. try {
  492. // first get the IP addresses if we don't have them yet
  493. // this is because we need the IP address to then get
  494. // FQDN.
  495. if (addresses == null) {
  496. getIP();
  497. }
  498. // we have to do this check, otherwise we might not
  499. // get the fully qualified domain name
  500. if (init_with_ip) {
  501. cname = addresses[0].getHostName(false).toLowerCase();
  502. } else {
  503. cname = InetAddress.getByName(addresses[0].getHostAddress()).
  504. getHostName(false).toLowerCase();
  505. }
  506. } catch (UnknownHostException uhe) {
  507. invalid = true;
  508. throw uhe;
  509. }
  510. }
  511. /**
  512. * get IP addresses. Sets invalid to true if we can't get them.
  513. *
  514. */
  515. void getIP()
  516. throws UnknownHostException
  517. {
  518. if (addresses != null || wildcard || invalid) return;
  519. try {
  520. // now get all the IP addresses
  521. String host;
  522. if (getName().charAt(0) == '[') {
  523. // Literal IPv6 address
  524. host = getName().substring(1, getName().indexOf(']'));
  525. } else {
  526. int i = getName().indexOf(":");
  527. if (i == -1)
  528. host = getName();
  529. else {
  530. host = getName().substring(0,i);
  531. }
  532. }
  533. addresses =
  534. new InetAddress[] {InetAddress.getAllByName0(host, false)[0]};
  535. } catch (UnknownHostException uhe) {
  536. invalid = true;
  537. throw uhe;
  538. } catch (IndexOutOfBoundsException iobe) {
  539. invalid = true;
  540. throw new UnknownHostException(getName());
  541. }
  542. }
  543. /**
  544. * Checks if this socket permission object "implies" the
  545. * specified permission.
  546. * <P>
  547. * More specifically, this method first ensures that all of the following
  548. * are true (and returns false if any of them are not):<p>
  549. * <ul>
  550. * <li> <i>p</i> is an instanceof SocketPermission,<p>
  551. * <li> <i>p</i>'s actions are a proper subset of this
  552. * object's actions, and<p>
  553. * <li> <i>p</i>'s port range is included in this port range. Note:
  554. * port range is ignored when p only contains the action, 'resolve'.<p>
  555. * </ul>
  556. *
  557. * Then <code>implies</code> checks each of the following, in order,
  558. * and for each returns true if the stated condition is true:<p>
  559. * <ul>
  560. * <li> If this object was initialized with a single IP address and one of <i>p</i>'s
  561. * IP addresses is equal to this object's IP address.<p>
  562. * <li>If this object is a wildcard domain (such as *.sun.com), and
  563. * <i>p</i>'s canonical name (the name without any preceding *)
  564. * ends with this object's canonical host name. For example, *.sun.com
  565. * implies *.eng.sun.com..<p>
  566. * <li>If this object was not initialized with a single IP address, and one of this
  567. * object's IP addresses equals one of <i>p</i>'s IP addresses.<p>
  568. * <li>If this canonical name equals <i>p</i>'s canonical name.<p>
  569. * </ul>
  570. *
  571. * If none of the above are true, <code>implies</code> returns false.
  572. * @param p the permission to check against.
  573. *
  574. * @return true if the specified permission is implied by this object,
  575. * false if not.
  576. */
  577. public boolean implies(Permission p) {
  578. int i,j;
  579. if (!(p instanceof SocketPermission))
  580. return false;
  581. SocketPermission that = (SocketPermission) p;
  582. return ((this.mask & that.mask) == that.mask) &&
  583. impliesIgnoreMask(that);
  584. }
  585. /**
  586. * Checks if the incoming Permission's action are a proper subset of
  587. * the this object's actions.
  588. * <P>
  589. * Check, in the following order:
  590. * <ul>
  591. * <li> Checks that "p" is an instanceof a SocketPermission
  592. * <li> Checks that "p"'s actions are a proper subset of the
  593. * current object's actions.
  594. * <li> Checks that "p"'s port range is included in this port range
  595. * <li> If this object was initialized with an IP address, checks that
  596. * one of "p"'s IP addresses is equal to this object's IP address.
  597. * <li> If either object is a wildcard domain (i.e., "*.sun.com"),
  598. * attempt to match based on the wildcard.
  599. * <li> If this object was not initialized with an IP address, attempt
  600. * to find a match based on the IP addresses in both objects.
  601. * <li> Attempt to match on the canonical hostnames of both objects.
  602. * </ul>
  603. * @param p the incoming permission request
  604. *
  605. * @return true if "permission" is a proper subset of the current object,
  606. * false if not.
  607. */
  608. boolean impliesIgnoreMask(SocketPermission that) {
  609. int i,j;
  610. if ((that.mask & RESOLVE) != that.mask) {
  611. // check port range
  612. if ((that.portrange[0] < this.portrange[0]) ||
  613. (that.portrange[1] > this.portrange[1])) {
  614. return false;
  615. }
  616. }
  617. // allow a "*" wildcard to always match anything
  618. if (this.wildcard && "".equals(this.cname))
  619. return true;
  620. // return if either one of these NetPerm objects are invalid...
  621. if (this.invalid || that.invalid) {
  622. return (trustProxy ? inProxyWeTrust(that) : false);
  623. }
  624. try {
  625. if (this.init_with_ip) { // we only check IP addresses
  626. if (that.wildcard)
  627. return false;
  628. if (that.init_with_ip) {
  629. return (this.addresses[0].equals(that.addresses[0]));
  630. } else {
  631. if (that.addresses == null) {
  632. that.getIP();
  633. }
  634. for (i=0; i < that.addresses.length; i++) {
  635. if (this.addresses[0].equals(that.addresses[i]))
  636. return true;
  637. }
  638. }
  639. // since "this" was initialized with an IP address, we
  640. // don't check any other cases
  641. return false;
  642. }
  643. // check and see if we have any wildcards...
  644. if (this.wildcard || that.wildcard) {
  645. // if they are both wildcards, return true iff
  646. // that's cname ends with this cname (i.e., *.sun.com
  647. // implies *.eng.sun.com)
  648. if (this.wildcard && that.wildcard)
  649. return (that.cname.endsWith(this.cname));
  650. // a non-wildcard can't imply a wildcard
  651. if (that.wildcard)
  652. return false;
  653. // this is a wildcard, lets see if that's cname ends with
  654. // it...
  655. if (that.cname == null) {
  656. that.getCanonName();
  657. }
  658. return (that.cname.endsWith(this.cname));
  659. }
  660. // comapare IP addresses
  661. if (this.addresses == null) {
  662. this.getIP();
  663. }
  664. if (that.addresses == null) {
  665. that.getIP();
  666. }
  667. for (j = 0; j < this.addresses.length; j++) {
  668. for (i=0; i < that.addresses.length; i++) {
  669. if (this.addresses[j].equals(that.addresses[i]))
  670. return true;
  671. }
  672. }
  673. // XXX: if all else fails, compare hostnames?
  674. // Do we really want this?
  675. if (this.cname == null) {
  676. this.getCanonName();
  677. }
  678. if (that.cname == null) {
  679. that.getCanonName();
  680. }
  681. return (this.cname.equalsIgnoreCase(that.cname));
  682. } catch (UnknownHostException uhe) {
  683. if (trustProxy)
  684. return inProxyWeTrust(that);
  685. }
  686. // make sure the first thing that is done here is to return
  687. // false. If not, uncomment the return false in the above catch.
  688. return false;
  689. }
  690. private boolean inProxyWeTrust(SocketPermission that) {
  691. // if we trust the proxy, we see if the original names/IPs passed
  692. // in were equal.
  693. String thisHost = hostname;
  694. String thatHost = that.hostname;
  695. if (thisHost == null)
  696. return false;
  697. else
  698. return thisHost.equalsIgnoreCase(thatHost);
  699. }
  700. /**
  701. * Checks two SocketPermission objects for equality.
  702. * <P>
  703. * @param obj the object to test for equality with this object.
  704. *
  705. * @return true if <i>obj</i> is a SocketPermission, and has the
  706. * same hostname, port range, and actions as this
  707. * SocketPermission object. However, port range will be ignored
  708. * in the comparison if <i>obj</i> only contains the action, 'resolve'.
  709. */
  710. public boolean equals(Object obj) {
  711. if (obj == this)
  712. return true;
  713. if (! (obj instanceof SocketPermission))
  714. return false;
  715. SocketPermission that = (SocketPermission) obj;
  716. //this is (overly?) complex!!!
  717. // check the mask first
  718. if (this.mask != that.mask) return false;
  719. if ((that.mask & RESOLVE) != that.mask) {
  720. // now check the port range...
  721. if ((this.portrange[0] != that.portrange[0]) ||
  722. (this.portrange[1] != that.portrange[1])) {
  723. return false;
  724. }
  725. }
  726. // short cut. This catches:
  727. // "crypto" equal to "crypto", or
  728. // "1.2.3.4" equal to "1.2.3.4.", or
  729. // "*.edu" equal to "*.edu", but it
  730. // does not catch "crypto" equal to
  731. // "crypto.eng.sun.com".
  732. if (this.getName().equalsIgnoreCase(that.getName())) {
  733. return true;
  734. }
  735. // we now attempt to get the Canonical (FQDN) name and
  736. // compare that. If this fails, about all we can do is return
  737. // false.
  738. try {
  739. this.getCanonName();
  740. that.getCanonName();
  741. } catch (UnknownHostException uhe) {
  742. return false;
  743. }
  744. if (this.invalid || that.invalid)
  745. return false;
  746. if (this.cname != null) {
  747. return this.cname.equalsIgnoreCase(that.cname);
  748. }
  749. return false;
  750. }
  751. /**
  752. * Returns the hash code value for this object.
  753. *
  754. * @return a hash code value for this object.
  755. */
  756. public int hashCode() {
  757. /*
  758. * If this SocketPermission was initialized with an IP address
  759. * or a wildcard, use getName().hashCode(), otherwise use
  760. * the hashCode() of the host name returned from
  761. * java.net.InetAddress.getHostName method.
  762. */
  763. if (init_with_ip || wildcard) {
  764. return this.getName().hashCode();
  765. }
  766. try {
  767. getCanonName();
  768. } catch (UnknownHostException uhe) {
  769. }
  770. if (invalid || cname == null)
  771. return this.getName().hashCode();
  772. else
  773. return this.cname.hashCode();
  774. }
  775. /**
  776. * Return the current action mask.
  777. *
  778. * @return the actions mask.
  779. */
  780. int getMask() {
  781. return mask;
  782. }
  783. /**
  784. * Returns the "canonical string representation" of the actions in the
  785. * specified mask.
  786. * Always returns present actions in the following order:
  787. * connect, listen, accept, resolve.
  788. *
  789. * @param mask a specific integer action mask to translate into a string
  790. * @return the canonical string representation of the actions
  791. */
  792. private static String getActions(int mask)
  793. {
  794. StringBuilder sb = new StringBuilder();
  795. boolean comma = false;
  796. if ((mask & CONNECT) == CONNECT) {
  797. comma = true;
  798. sb.append("connect");
  799. }
  800. if ((mask & LISTEN) == LISTEN) {
  801. if (comma) sb.append(',');
  802. else comma = true;
  803. sb.append("listen");
  804. }
  805. if ((mask & ACCEPT) == ACCEPT) {
  806. if (comma) sb.append(',');
  807. else comma = true;
  808. sb.append("accept");
  809. }
  810. if ((mask & RESOLVE) == RESOLVE) {
  811. if (comma) sb.append(',');
  812. else comma = true;
  813. sb.append("resolve");
  814. }
  815. return sb.toString();
  816. }
  817. /**
  818. * Returns the canonical string representation of the actions.
  819. * Always returns present actions in the following order:
  820. * connect, listen, accept, resolve.
  821. *
  822. * @return the canonical string representation of the actions.
  823. */
  824. public String getActions()
  825. {
  826. if (actions == null)
  827. actions = getActions(this.mask);
  828. return actions;
  829. }
  830. /**
  831. * Returns a new PermissionCollection object for storing SocketPermission
  832. * objects.
  833. * <p>
  834. * SocketPermission objects must be stored in a manner that allows them
  835. * to be inserted into the collection in any order, but that also enables the
  836. * PermissionCollection <code>implies</code>
  837. * method to be implemented in an efficient (and consistent) manner.
  838. *
  839. * @return a new PermissionCollection object suitable for storing SocketPermissions.
  840. */
  841. public PermissionCollection newPermissionCollection() {
  842. return new SocketPermissionCollection();
  843. }
  844. /**
  845. * WriteObject is called to save the state of the SocketPermission
  846. * to a stream. The actions are serialized, and the superclass
  847. * takes care of the name.
  848. */
  849. private synchronized void writeObject(java.io.ObjectOutputStream s)
  850. throws IOException
  851. {
  852. // Write out the actions. The superclass takes care of the name
  853. // call getActions to make sure actions field is initialized
  854. if (actions == null)
  855. getActions();
  856. s.defaultWriteObject();
  857. }
  858. /**
  859. * readObject is called to restore the state of the SocketPermission from
  860. * a stream.
  861. */
  862. private synchronized void readObject(java.io.ObjectInputStream s)
  863. throws IOException, ClassNotFoundException
  864. {
  865. // Read in the action, then initialize the rest
  866. s.defaultReadObject();
  867. init(getName(),getMask(actions));
  868. }
  869. /*
  870. public String toString()
  871. {
  872. StringBuffer s = new StringBuffer(super.toString() + "\n" +
  873. "cname = " + cname + "\n" +
  874. "wildcard = " + wildcard + "\n" +
  875. "invalid = " + invalid + "\n" +
  876. "portrange = " + portrange[0] + "," + portrange[1] + "\n");
  877. if (addresses != null) for (int i=0; i<addresses.length; i++) {
  878. s.append( addresses[i].getHostAddress());
  879. s.append("\n");
  880. } else {
  881. s.append("(no addresses)\n");
  882. }
  883. return s.toString();
  884. }
  885. public static void main(String args[]) throws Exception {
  886. SocketPermission this_ = new SocketPermission(args[0], "connect");
  887. SocketPermission that_ = new SocketPermission(args[1], "connect");
  888. System.out.println("-----\n");
  889. System.out.println("this.implies(that) = " + this_.implies(that_));
  890. System.out.println("-----\n");
  891. System.out.println("this = "+this_);
  892. System.out.println("-----\n");
  893. System.out.println("that = "+that_);
  894. System.out.println("-----\n");
  895. SocketPermissionCollection nps = new SocketPermissionCollection();
  896. nps.add(this_);
  897. nps.add(new SocketPermission("www-leland.stanford.edu","connect"));
  898. nps.add(new SocketPermission("www-sun.com","connect"));
  899. System.out.println("nps.implies(that) = " + nps.implies(that_));
  900. System.out.println("-----\n");
  901. }
  902. */
  903. }
  904. /**
  905. if (init'd with IP, key is IP as string)
  906. if wildcard, its the wild card
  907. else its the cname?
  908. *
  909. * @see java.security.Permission
  910. * @see java.security.Permissions
  911. * @see java.security.PermissionCollection
  912. *
  913. * @version 1.59 02/12/04
  914. *
  915. * @author Roland Schemers
  916. *
  917. * @serial include
  918. */
  919. final class SocketPermissionCollection extends PermissionCollection
  920. implements Serializable
  921. {
  922. // Not serialized; see serialization section at end of class
  923. private transient List perms;
  924. /**
  925. * Create an empty SocketPermissions object.
  926. *
  927. */
  928. public SocketPermissionCollection() {
  929. perms = new ArrayList();
  930. }
  931. /**
  932. * Adds a permission to the SocketPermissions. The key for the hash is
  933. * the name in the case of wildcards, or all the IP addresses.
  934. *
  935. * @param permission the Permission object to add.
  936. *
  937. * @exception IllegalArgumentException - if the permission is not a
  938. * SocketPermission
  939. *
  940. * @exception SecurityException - if this SocketPermissionCollection object
  941. * has been marked readonly
  942. */
  943. public void add(Permission permission)
  944. {
  945. if (! (permission instanceof SocketPermission))
  946. throw new IllegalArgumentException("invalid permission: "+
  947. permission);
  948. if (isReadOnly())
  949. throw new SecurityException(
  950. "attempt to add a Permission to a readonly PermissionCollection");
  951. // optimization to ensure perms most likely to be tested
  952. // show up early (4301064)
  953. synchronized (this) {
  954. perms.add(0, permission);
  955. }
  956. }
  957. /**
  958. * Check and see if this collection of permissions implies the permissions
  959. * expressed in "permission".
  960. *
  961. * @param p the Permission object to compare
  962. *
  963. * @return true if "permission" is a proper subset of a permission in
  964. * the collection, false if not.
  965. */
  966. public boolean implies(Permission permission)
  967. {
  968. if (! (permission instanceof SocketPermission))
  969. return false;
  970. SocketPermission np = (SocketPermission) permission;
  971. int desired = np.getMask();
  972. int effective = 0;
  973. int needed = desired;
  974. synchronized (this) {
  975. int len = perms.size();
  976. //System.out.println("implies "+np);
  977. for (int i = 0; i < len; i++) {
  978. SocketPermission x = (SocketPermission) perms.get(i);
  979. //System.out.println(" trying "+x);
  980. if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(np)) {
  981. effective |= x.getMask();
  982. if ((effective & desired) == desired)
  983. return true;
  984. needed = (desired ^ effective);
  985. }
  986. }
  987. }
  988. return false;
  989. }
  990. /**
  991. * Returns an enumeration of all the SocketPermission objects in the
  992. * container.
  993. *
  994. * @return an enumeration of all the SocketPermission objects.
  995. */
  996. public Enumeration elements() {
  997. // Convert Iterator into Enumeration
  998. synchronized (this) {
  999. return Collections.enumeration(perms);
  1000. }
  1001. }
  1002. private static final long serialVersionUID = 2787186408602843674L;
  1003. // Need to maintain serialization interoperability with earlier releases,
  1004. // which had the serializable field:
  1005. //
  1006. // The SocketPermissions for this set.
  1007. // @serial
  1008. //
  1009. // private Vector permissions;
  1010. /**
  1011. * @serialField permissions java.util.Vector
  1012. * A list of the SocketPermissions for this set.
  1013. */
  1014. private static final ObjectStreamField[] serialPersistentFields = {
  1015. new ObjectStreamField("permissions", Vector.class),
  1016. };
  1017. /**
  1018. * @serialData "permissions" field (a Vector containing the SocketPermissions).
  1019. */
  1020. /*
  1021. * Writes the contents of the perms field out as a Vector for
  1022. * serialization compatibility with earlier releases.
  1023. */
  1024. private void writeObject(ObjectOutputStream out) throws IOException {
  1025. // Don't call out.defaultWriteObject()
  1026. // Write out Vector
  1027. Vector permissions = new Vector(perms.size());
  1028. synchronized (this) {
  1029. permissions.addAll(perms);
  1030. }
  1031. ObjectOutputStream.PutField pfields = out.putFields();
  1032. pfields.put("permissions", permissions);
  1033. out.writeFields();
  1034. }
  1035. /*
  1036. * Reads in a Vector of SocketPermissions and saves them in the perms field.
  1037. */
  1038. private void readObject(ObjectInputStream in) throws IOException,
  1039. ClassNotFoundException {
  1040. // Don't call in.defaultReadObject()
  1041. // Read in serialized fields
  1042. ObjectInputStream.GetField gfields = in.readFields();
  1043. // Get the one we want
  1044. Vector permissions = (Vector)gfields.get("permissions", null);
  1045. perms = new ArrayList(permissions.size());
  1046. perms.addAll(permissions);
  1047. }
  1048. }