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