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