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

Save This Page
Home » openjdk-7 » java » net » [javadoc | source]