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