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