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