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