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