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&nbsp;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 != nullfor (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