History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/net/SocketPermission.java (1 of 18352 files). Show all

Unified · Split

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