View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, 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.util.Enumeration;
29  import java.util.Vector;
30  import java.util.List;
31  import java.util.ArrayList;
32  import java.util.Collections;
33  import java.util.StringTokenizer;
34  import java.net.InetAddress;
35  import java.security.Permission;
36  import java.security.PermissionCollection;
37  import java.security.PrivilegedAction;
38  import java.security.AccessController;
39  import java.security.Security;
40  import java.io.Serializable;
41  import java.io.ObjectStreamField;
42  import java.io.ObjectOutputStream;
43  import java.io.ObjectInputStream;
44  import java.io.IOException;
45  import sun.net.util.IPAddressUtil;
46  import sun.net.RegisteredDomain;
47  import sun.net.PortConfig;
48  import sun.security.util.SecurityConstants;
49  import sun.security.util.Debug;
50  
51  
52  /**
53   * This class represents access to a network via sockets.
54   * A SocketPermission consists of a
55   * host specification and a set of "actions" specifying ways to
56   * connect to that host. The host is specified as
57   * <pre>
58   *    host = (hostname | IPv4address | iPv6reference) [:portrange]
59   *    portrange = portnumber | -portnumber | portnumber-[portnumber]
60   * </pre>
61   * The host is expressed as a DNS name, as a numerical IP address,
62   * or as "localhost" (for the local machine).
63   * The wildcard "*" may be included once in a DNS name host
64   * specification. If it is included, it must be in the leftmost
65   * position, as in "*.sun.com".
66   * <p>
67   * The format of the IPv6reference should follow that specified in <a
68   * href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC&nbsp;2732: Format
69   * for Literal IPv6 Addresses in URLs</i></a>:
70   * <pre>
71   *    ipv6reference = "[" IPv6address "]"
72   *</pre>
73   * For example, you can construct a SocketPermission instance
74   * as the following:
75   * <pre>
76   *    String hostAddress = inetaddress.getHostAddress();
77   *    if (inetaddress instanceof Inet6Address) {
78   *        sp = new SocketPermission("[" + hostAddress + "]:" + port, action);
79   *    } else {
80   *        sp = new SocketPermission(hostAddress + ":" + port, action);
81   *    }
82   * </pre>
83   * or
84   * <pre>
85   *    String host = url.getHost();
86   *    sp = new SocketPermission(host + ":" + port, action);
87   * </pre>
88   * <p>
89   * The <A HREF="Inet6Address.html#lform">full uncompressed form</A> of
90   * an IPv6 literal address is also valid.
91   * <p>
92   * The port or portrange is optional. A port specification of the
93   * form "N-", where <i>N</i> is a port number, signifies all ports
94   * numbered <i>N</i> and above, while a specification of the
95   * form "-N" indicates all ports numbered <i>N</i> and below.
96   * The special port value {@code 0} refers to the entire <i>ephemeral</i>
97   * port range. This is a fixed range of ports a system may use to
98   * allocate dynamic ports from. The actual range may be system dependent.
99   * <p>
100  * The possible ways to connect to the host are
101  * <pre>
102  * accept
103  * connect
104  * listen
105  * resolve
106  * </pre>
107  * The "listen" action is only meaningful when used with "localhost" and
108  * means the ability to bind to a specified port.
109  * The "resolve" action is implied when any of the other actions are present.
110  * The action "resolve" refers to host/ip name service lookups.
111  * <P>
112  * The actions string is converted to lowercase before processing.
113  * <p>As an example of the creation and meaning of SocketPermissions,
114  * note that if the following permission:
115  *
116  * <pre>
117  *   p1 = new SocketPermission("puffin.eng.sun.com:7777", "connect,accept");
118  * </pre>
119  *
120  * is granted to some code, it allows that code to connect to port 7777 on
121  * {@code puffin.eng.sun.com}, and to accept connections on that port.
122  *
123  * <p>Similarly, if the following permission:
124  *
125  * <pre>
126  *   p2 = new SocketPermission("localhost:1024-", "accept,connect,listen");
127  * </pre>
128  *
129  * is granted to some code, it allows that code to
130  * accept connections on, connect to, or listen on any port between
131  * 1024 and 65535 on the local host.
132  *
133  * <p>Note: Granting code permission to accept or make connections to remote
134  * hosts may be dangerous because malevolent code can then more easily
135  * transfer and share confidential data among parties who may not
136  * otherwise have access to the data.
137  *
138  * @see java.security.Permissions
139  * @see SocketPermission
140  *
141  *
142  * @author Marianne Mueller
143  * @author Roland Schemers
144  *
145  * @serial exclude
146  */
147 
148 public final class SocketPermission extends Permission
149     implements java.io.Serializable
150 {
151     private static final long serialVersionUID = -7204263841984476862L;
152 
153     /**
154      * Connect to host:port
155      */
156     private final static int CONNECT    = 0x1;
157 
158     /**
159      * Listen on host:port
160      */
161     private final static int LISTEN     = 0x2;
162 
163     /**
164      * Accept a connection from host:port
165      */
166     private final static int ACCEPT     = 0x4;
167 
168     /**
169      * Resolve DNS queries
170      */
171     private final static int RESOLVE    = 0x8;
172 
173     /**
174      * No actions
175      */
176     private final static int NONE               = 0x0;
177 
178     /**
179      * All actions
180      */
181     private final static int ALL        = CONNECT|LISTEN|ACCEPT|RESOLVE;
182 
183     // various port constants
184     private static final int PORT_MIN = 0;
185     private static final int PORT_MAX = 65535;
186     private static final int PRIV_PORT_MAX = 1023;
187     private static final int DEF_EPH_LOW = 49152;
188 
189     // the actions mask
190     private transient int mask;
191 
192     /**
193      * the actions string.
194      *
195      * @serial
196      */
197 
198     private String actions; // Left null as long as possible, then
199                             // created and re-used in the getAction function.
200 
201     // hostname part as it is passed
202     private transient String hostname;
203 
204     // the canonical name of the host
205     // in the case of "*.foo.com", cname is ".foo.com".
206 
207     private transient String cname;
208 
209     // all the IP addresses of the host
210     private transient InetAddress[] addresses;
211 
212     // true if the hostname is a wildcard (e.g. "*.sun.com")
213     private transient boolean wildcard;
214 
215     // true if we were initialized with a single numeric IP address
216     private transient boolean init_with_ip;
217 
218     // true if this SocketPermission represents an invalid/unknown host
219     // used for implies when the delayed lookup has already failed
220     private transient boolean invalid;
221 
222     // port range on host
223     private transient int[] portrange;
224 
225     private transient boolean defaultDeny = false;
226 
227     // true if this SocketPermission represents a hostname
228     // that failed our reverse mapping heuristic test
229     private transient boolean untrusted;
230     private transient boolean trusted;
231 
232     // true if the sun.net.trustNameService system property is set
233     private static boolean trustNameService;
234 
235     private static Debug debug = null;
236     private static boolean debugInit = false;
237 
238     // ephemeral port range for this system
239     private static final int ephemeralLow = initEphemeralPorts(
240         "low", DEF_EPH_LOW
241     );
242     private static final int ephemeralHigh = initEphemeralPorts(
243         "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.length() > 0) {
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     private boolean match(String cname, String hname) {
679         String a = cname.toLowerCase();
680         String b = hname.toLowerCase();
681         if (a.startsWith(b)  &&
682             ((a.length() == b.length()) || (a.charAt(b.length()) == '.')))
683             return true;
684         if (cdomain == null) {
685             cdomain = RegisteredDomain.getRegisteredDomain(a);
686         }
687         if (hdomain == null) {
688             hdomain = RegisteredDomain.getRegisteredDomain(b);
689         }
690 
691         return cdomain.length() != 0 && hdomain.length() != 0
692                         && cdomain.equals(hdomain);
693     }
694 
695     private boolean authorized(String cname, byte[] addr) {
696         if (addr.length == 4)
697             return authorizedIPv4(cname, addr);
698         else if (addr.length == 16)
699             return authorizedIPv6(cname, addr);
700         else
701             return false;
702     }
703 
704     private boolean authorizedIPv4(String cname, byte[] addr) {
705         String authHost = "";
706         InetAddress auth;
707 
708         try {
709             authHost = "auth." +
710                         (addr[3] & 0xff) + "." + (addr[2] & 0xff) + "." +
711                         (addr[1] & 0xff) + "." + (addr[0] & 0xff) +
712                         ".in-addr.arpa";
713             // Following check seems unnecessary
714             // auth = InetAddress.getAllByName0(authHost, false)[0];
715             authHost = hostname + '.' + authHost;
716             auth = InetAddress.getAllByName0(authHost, false)[0];
717             if (auth.equals(InetAddress.getByAddress(addr))) {
718                 return true;
719             }
720             Debug debug = getDebug();
721             if (debug != null && Debug.isOn("failure")) {
722                 debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr));
723             }
724         } catch (UnknownHostException uhe) {
725             Debug debug = getDebug();
726             if (debug != null && Debug.isOn("failure")) {
727                 debug.println("socket access restriction: forward lookup failed for " + authHost);
728             }
729         }
730         return false;
731     }
732 
733     private boolean authorizedIPv6(String cname, byte[] addr) {
734         String authHost = "";
735         InetAddress auth;
736 
737         try {
738             StringBuffer sb = new StringBuffer(39);
739 
740             for (int i = 15; i >= 0; i--) {
741                 sb.append(Integer.toHexString(((addr[i]) & 0x0f)));
742                 sb.append('.');
743                 sb.append(Integer.toHexString(((addr[i] >> 4) & 0x0f)));
744                 sb.append('.');
745             }
746             authHost = "auth." + sb.toString() + "IP6.ARPA";
747             //auth = InetAddress.getAllByName0(authHost, false)[0];
748             authHost = hostname + '.' + authHost;
749             auth = InetAddress.getAllByName0(authHost, false)[0];
750             if (auth.equals(InetAddress.getByAddress(addr)))
751                 return true;
752             Debug debug = getDebug();
753             if (debug != null && Debug.isOn("failure")) {
754                 debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr));
755             }
756         } catch (UnknownHostException uhe) {
757             Debug debug = getDebug();
758             if (debug != null && Debug.isOn("failure")) {
759                 debug.println("socket access restriction: forward lookup failed for " + authHost);
760             }
761         }
762         return false;
763     }
764 
765 
766     /**
767      * get IP addresses. Sets invalid to true if we can't get them.
768      *
769      */
770     void getIP()
771         throws UnknownHostException
772     {
773         if (addresses != null || wildcard || invalid) return;
774 
775         try {
776             // now get all the IP addresses
777             String host;
778             if (getName().charAt(0) == '[') {
779                 // Literal IPv6 address
780                 host = getName().substring(1, getName().indexOf(']'));
781             } else {
782                 int i = getName().indexOf(":");
783                 if (i == -1)
784                     host = getName();
785                 else {
786                     host = getName().substring(0,i);
787                 }
788             }
789 
790             addresses =
791                 new InetAddress[] {InetAddress.getAllByName0(host, false)[0]};
792 
793         } catch (UnknownHostException uhe) {
794             invalid = true;
795             throw uhe;
796         }  catch (IndexOutOfBoundsException iobe) {
797             invalid = true;
798             throw new UnknownHostException(getName());
799         }
800     }
801 
802     /**
803      * Checks if this socket permission object "implies" the
804      * specified permission.
805      * <P>
806      * More specifically, this method first ensures that all of the following
807      * are true (and returns false if any of them are not):
808      * <ul>
809      * <li> <i>p</i> is an instanceof SocketPermission,
810      * <li> <i>p</i>'s actions are a proper subset of this
811      * object's actions, and
812      * <li> <i>p</i>'s port range is included in this port range. Note:
813      * port range is ignored when p only contains the action, 'resolve'.
814      * </ul>
815      *
816      * Then {@code implies} checks each of the following, in order,
817      * and for each returns true if the stated condition is true:
818      * <ul>
819      * <li> If this object was initialized with a single IP address and one of <i>p</i>'s
820      * IP addresses is equal to this object's IP address.
821      * <li>If this object is a wildcard domain (such as *.sun.com), and
822      * <i>p</i>'s canonical name (the name without any preceding *)
823      * ends with this object's canonical host name. For example, *.sun.com
824      * implies *.eng.sun.com.
825      * <li>If this object was not initialized with a single IP address, and one of this
826      * object's IP addresses equals one of <i>p</i>'s IP addresses.
827      * <li>If this canonical name equals <i>p</i>'s canonical name.
828      * </ul>
829      *
830      * If none of the above are true, {@code implies} returns false.
831      * @param p the permission to check against.
832      *
833      * @return true if the specified permission is implied by this object,
834      * false if not.
835      */
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      * <P>
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     public boolean equals(Object obj) {
1015         if (obj == this)
1016             return true;
1017 
1018         if (! (obj instanceof SocketPermission))
1019             return false;
1020 
1021         SocketPermission that = (SocketPermission) obj;
1022 
1023         //this is (overly?) complex!!!
1024 
1025         // check the mask first
1026         if (this.mask != that.mask) return false;
1027 
1028         if ((that.mask & RESOLVE) != that.mask) {
1029             // now check the port range...
1030             if ((this.portrange[0] != that.portrange[0]) ||
1031                 (this.portrange[1] != that.portrange[1])) {
1032                 return false;
1033             }
1034         }
1035 
1036         // short cut. This catches:
1037         //  "crypto" equal to "crypto", or
1038         // "1.2.3.4" equal to "1.2.3.4.", or
1039         //  "*.edu" equal to "*.edu", but it
1040         //  does not catch "crypto" equal to
1041         // "crypto.eng.sun.com".
1042 
1043         if (this.getName().equalsIgnoreCase(that.getName())) {
1044             return true;
1045         }
1046 
1047         // we now attempt to get the Canonical (FQDN) name and
1048         // compare that. If this fails, about all we can do is return
1049         // false.
1050 
1051         try {
1052             this.getCanonName();
1053             that.getCanonName();
1054         } catch (UnknownHostException uhe) {
1055             return false;
1056         }
1057 
1058         if (this.invalid || that.invalid)
1059             return false;
1060 
1061         if (this.cname != null) {
1062             return this.cname.equalsIgnoreCase(that.cname);
1063         }
1064 
1065         return false;
1066     }
1067 
1068     /**
1069      * Returns the hash code value for this object.
1070      *
1071      * @return a hash code value for this object.
1072      */
1073 
1074     public int hashCode() {
1075         /*
1076          * If this SocketPermission was initialized with an IP address
1077          * or a wildcard, use getName().hashCode(), otherwise use
1078          * the hashCode() of the host name returned from
1079          * java.net.InetAddress.getHostName method.
1080          */
1081 
1082         if (init_with_ip || wildcard) {
1083             return this.getName().hashCode();
1084         }
1085 
1086         try {
1087             getCanonName();
1088         } catch (UnknownHostException uhe) {
1089 
1090         }
1091 
1092         if (invalid || cname == null)
1093             return this.getName().hashCode();
1094         else
1095             return this.cname.hashCode();
1096     }
1097 
1098     /**
1099      * Return the current action mask.
1100      *
1101      * @return the actions mask.
1102      */
1103 
1104     int getMask() {
1105         return mask;
1106     }
1107 
1108     /**
1109      * Returns the "canonical string representation" of the actions in the
1110      * specified mask.
1111      * Always returns present actions in the following order:
1112      * connect, listen, accept, resolve.
1113      *
1114      * @param mask a specific integer action mask to translate into a string
1115      * @return the canonical string representation of the actions
1116      */
1117     private static String getActions(int mask)
1118     {
1119         StringBuilder sb = new StringBuilder();
1120         boolean comma = false;
1121 
1122         if ((mask & CONNECT) == CONNECT) {
1123             comma = true;
1124             sb.append("connect");
1125         }
1126 
1127         if ((mask & LISTEN) == LISTEN) {
1128             if (comma) sb.append(',');
1129             else comma = true;
1130             sb.append("listen");
1131         }
1132 
1133         if ((mask & ACCEPT) == ACCEPT) {
1134             if (comma) sb.append(',');
1135             else comma = true;
1136             sb.append("accept");
1137         }
1138 
1139 
1140         if ((mask & RESOLVE) == RESOLVE) {
1141             if (comma) sb.append(',');
1142             else comma = true;
1143             sb.append("resolve");
1144         }
1145 
1146         return sb.toString();
1147     }
1148 
1149     /**
1150      * Returns the canonical string representation of the actions.
1151      * Always returns present actions in the following order:
1152      * connect, listen, accept, resolve.
1153      *
1154      * @return the canonical string representation of the actions.
1155      */
1156     public String getActions()
1157     {
1158         if (actions == null)
1159             actions = getActions(this.mask);
1160 
1161         return actions;
1162     }
1163 
1164     /**
1165      * Returns a new PermissionCollection object for storing SocketPermission
1166      * objects.
1167      * <p>
1168      * SocketPermission objects must be stored in a manner that allows them
1169      * to be inserted into the collection in any order, but that also enables the
1170      * PermissionCollection {@code implies}
1171      * method to be implemented in an efficient (and consistent) manner.
1172      *
1173      * @return a new PermissionCollection object suitable for storing SocketPermissions.
1174      */
1175 
1176     public PermissionCollection newPermissionCollection() {
1177         return new SocketPermissionCollection();
1178     }
1179 
1180     /**
1181      * WriteObject is called to save the state of the SocketPermission
1182      * to a stream. The actions are serialized, and the superclass
1183      * takes care of the name.
1184      */
1185     private synchronized void writeObject(java.io.ObjectOutputStream s)
1186         throws IOException
1187     {
1188         // Write out the actions. The superclass takes care of the name
1189         // call getActions to make sure actions field is initialized
1190         if (actions == null)
1191             getActions();
1192         s.defaultWriteObject();
1193     }
1194 
1195     /**
1196      * readObject is called to restore the state of the SocketPermission from
1197      * a stream.
1198      */
1199     private synchronized void readObject(java.io.ObjectInputStream s)
1200          throws IOException, ClassNotFoundException
1201     {
1202         // Read in the action, then initialize the rest
1203         s.defaultReadObject();
1204         init(getName(),getMask(actions));
1205     }
1206 
1207     /**
1208      * Check the system/security property for the ephemeral port range
1209      * for this system. The suffix is either "high" or "low"
1210      */
1211     private static int initEphemeralPorts(String suffix, int defval) {
1212         return AccessController.doPrivileged(
1213             new PrivilegedAction<Integer>(){
1214                 public Integer run() {
1215                     int val = Integer.getInteger(
1216                             "jdk.net.ephemeralPortRange."+suffix, -1
1217                     );
1218                     if (val != -1) {
1219                         return val;
1220                     } else {
1221                         return suffix.equals("low") ?
1222                             PortConfig.getLower() : PortConfig.getUpper();
1223                     }
1224                 }
1225             }
1226         );
1227     }
1228 
1229     /**
1230      * Check if the target range is within the policy range
1231      * together with the ephemeral range for this platform
1232      * (if policy includes ephemeral range)
1233      */
1234     private static boolean inRange(
1235         int policyLow, int policyHigh, int targetLow, int targetHigh
1236     )
1237     {
1238         if (targetLow == 0) {
1239             // check policy includes ephemeral range
1240             if (!inRange(policyLow, policyHigh, ephemeralLow, ephemeralHigh)) {
1241                 return false;
1242             }
1243             if (targetHigh == 0) {
1244                 // nothing left to do
1245                 return true;
1246             }
1247             // continue check with first real port number
1248             targetLow = 1;
1249         }
1250 
1251         if (policyLow == 0 && policyHigh == 0) {
1252             // ephemeral range only
1253             return targetLow >= ephemeralLow && targetHigh <= ephemeralHigh;
1254         }
1255 
1256         if (policyLow != 0) {
1257             // simple check of policy only
1258             return targetLow >= policyLow && targetHigh <= policyHigh;
1259         }
1260 
1261         // policyLow == 0 which means possibly two ranges to check
1262 
1263         // first check if policy and ephem range overlap/contiguous
1264 
1265         if (policyHigh >= ephemeralLow - 1) {
1266             return targetHigh <= ephemeralHigh;
1267         }
1268 
1269         // policy and ephem range do not overlap
1270 
1271         // target range must lie entirely inside policy range or eph range
1272 
1273         return  (targetLow <= policyHigh && targetHigh <= policyHigh) ||
1274                 (targetLow >= ephemeralLow && targetHigh <= ephemeralHigh);
1275     }
1276     /*
1277     public String toString()
1278     {
1279         StringBuffer s = new StringBuffer(super.toString() + "\n" +
1280             "cname = " + cname + "\n" +
1281             "wildcard = " + wildcard + "\n" +
1282             "invalid = " + invalid + "\n" +
1283             "portrange = " + portrange[0] + "," + portrange[1] + "\n");
1284         if (addresses != null) for (int i=0; i<addresses.length; i++) {
1285             s.append( addresses[i].getHostAddress());
1286             s.append("\n");
1287         } else {
1288             s.append("(no addresses)\n");
1289         }
1290 
1291         return s.toString();
1292     }
1293 
1294     public static void main(String args[]) throws Exception {
1295         SocketPermission this_ = new SocketPermission(args[0], "connect");
1296         SocketPermission that_ = new SocketPermission(args[1], "connect");
1297         System.out.println("-----\n");
1298         System.out.println("this.implies(that) = " + this_.implies(that_));
1299         System.out.println("-----\n");
1300         System.out.println("this = "+this_);
1301         System.out.println("-----\n");
1302         System.out.println("that = "+that_);
1303         System.out.println("-----\n");
1304 
1305         SocketPermissionCollection nps = new SocketPermissionCollection();
1306         nps.add(this_);
1307         nps.add(new SocketPermission("www-leland.stanford.edu","connect"));
1308         nps.add(new SocketPermission("www-sun.com","connect"));
1309         System.out.println("nps.implies(that) = " + nps.implies(that_));
1310         System.out.println("-----\n");
1311     }
1312     */
1313 }
1314 
1315 /**
1316 
1317 if (init'd with IP, key is IP as string)
1318 if wildcard, its the wild card
1319 else its the cname?
1320 
1321  *
1322  * @see java.security.Permission
1323  * @see java.security.Permissions
1324  * @see java.security.PermissionCollection
1325  *
1326  *
1327  * @author Roland Schemers
1328  *
1329  * @serial include
1330  */
1331 
1332 final class SocketPermissionCollection extends PermissionCollection
1333     implements Serializable
1334 {
1335     // Not serialized; see serialization section at end of class
1336     private transient List<SocketPermission> perms;
1337 
1338     /**
1339      * Create an empty SocketPermissions object.
1340      *
1341      */
1342 
1343     public SocketPermissionCollection() {
1344         perms = new ArrayList<SocketPermission>();
1345     }
1346 
1347     /**
1348      * Adds a permission to the SocketPermissions. The key for the hash is
1349      * the name in the case of wildcards, or all the IP addresses.
1350      *
1351      * @param permission the Permission object to add.
1352      *
1353      * @exception IllegalArgumentException - if the permission is not a
1354      *                                       SocketPermission
1355      *
1356      * @exception SecurityException - if this SocketPermissionCollection object
1357      *                                has been marked readonly
1358      */
1359     public void add(Permission permission) {
1360         if (! (permission instanceof SocketPermission))
1361             throw new IllegalArgumentException("invalid permission: "+
1362                                                permission);
1363         if (isReadOnly())
1364             throw new SecurityException(
1365                 "attempt to add a Permission to a readonly PermissionCollection");
1366 
1367         // optimization to ensure perms most likely to be tested
1368         // show up early (4301064)
1369         synchronized (this) {
1370             perms.add(0, (SocketPermission)permission);
1371         }
1372     }
1373 
1374     /**
1375      * Check and see if this collection of permissions implies the permissions
1376      * expressed in "permission".
1377      *
1378      * @param permission the Permission object to compare
1379      *
1380      * @return true if "permission" is a proper subset of a permission in
1381      * the collection, false if not.
1382      */
1383 
1384     public boolean implies(Permission permission)
1385     {
1386         if (! (permission instanceof SocketPermission))
1387                 return false;
1388 
1389         SocketPermission np = (SocketPermission) permission;
1390 
1391         int desired = np.getMask();
1392         int effective = 0;
1393         int needed = desired;
1394 
1395         synchronized (this) {
1396             int len = perms.size();
1397             //System.out.println("implies "+np);
1398             for (int i = 0; i < len; i++) {
1399                 SocketPermission x = perms.get(i);
1400                 //System.out.println("  trying "+x);
1401                 if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(np)) {
1402                     effective |=  x.getMask();
1403                     if ((effective & desired) == desired)
1404                         return true;
1405                     needed = (desired ^ effective);
1406                 }
1407             }
1408         }
1409         return false;
1410     }
1411 
1412     /**
1413      * Returns an enumeration of all the SocketPermission objects in the
1414      * container.
1415      *
1416      * @return an enumeration of all the SocketPermission objects.
1417      */
1418 
1419     @SuppressWarnings("unchecked")
1420     public Enumeration<Permission> elements() {
1421         // Convert Iterator into Enumeration
1422         synchronized (this) {
1423             return Collections.enumeration((List<Permission>)(List)perms);
1424         }
1425     }
1426 
1427     private static final long serialVersionUID = 2787186408602843674L;
1428 
1429     // Need to maintain serialization interoperability with earlier releases,
1430     // which had the serializable field:
1431 
1432     //
1433     // The SocketPermissions for this set.
1434     // @serial
1435     //
1436     // private Vector permissions;
1437 
1438     /**
1439      * @serialField permissions java.util.Vector
1440      *     A list of the SocketPermissions for this set.
1441      */
1442     private static final ObjectStreamField[] serialPersistentFields = {
1443         new ObjectStreamField("permissions", Vector.class),
1444     };
1445 
1446     /**
1447      * @serialData "permissions" field (a Vector containing the SocketPermissions).
1448      */
1449     /*
1450      * Writes the contents of the perms field out as a Vector for
1451      * serialization compatibility with earlier releases.
1452      */
1453     private void writeObject(ObjectOutputStream out) throws IOException {
1454         // Don't call out.defaultWriteObject()
1455 
1456         // Write out Vector
1457         Vector<SocketPermission> permissions = new Vector<>(perms.size());
1458 
1459         synchronized (this) {
1460             permissions.addAll(perms);
1461         }
1462 
1463         ObjectOutputStream.PutField pfields = out.putFields();
1464         pfields.put("permissions", permissions);
1465         out.writeFields();
1466     }
1467 
1468     /*
1469      * Reads in a Vector of SocketPermissions and saves them in the perms field.
1470      */
1471     private void readObject(ObjectInputStream in)
1472         throws IOException, ClassNotFoundException
1473     {
1474         // Don't call in.defaultReadObject()
1475 
1476         // Read in serialized fields
1477         ObjectInputStream.GetField gfields = in.readFields();
1478 
1479         // Get the one we want
1480         @SuppressWarnings("unchecked")
1481         Vector<SocketPermission> permissions = (Vector<SocketPermission>)gfields.get("permissions", null);
1482         perms = new ArrayList<SocketPermission>(permissions.size());
1483         perms.addAll(permissions);
1484     }
1485 }