6569621: Problem with java/classes_net
authormichaelm
Mon, 02 May 2011 11:02:58 +0100
changeset 9544 89c6ba8bb6c7
parent 9542 6f715a596a2e
child 9545 f5b408c1db04
6569621: Problem with java/classes_net Reviewed-by: chegar
jdk/src/share/classes/java/net/InetAddress.java
jdk/src/share/classes/java/net/Socket.java
jdk/src/share/classes/java/net/SocketPermission.java
jdk/src/share/classes/sun/net/www/URLConnection.java
jdk/src/share/classes/sun/net/www/http/HttpClient.java
--- a/jdk/src/share/classes/java/net/InetAddress.java	Sun May 01 14:22:32 2011 +0800
+++ b/jdk/src/share/classes/java/net/InetAddress.java	Mon May 02 11:02:58 2011 +0100
@@ -1013,6 +1013,12 @@
         return InetAddress.getAllByName(host)[0];
     }
 
+    // called from deployment cache manager
+    public static InetAddress getByName(String host, InetAddress reqAddr)
+        throws UnknownHostException {
+        return InetAddress.getAllByName(host, reqAddr)[0];
+    }
+
     /**
      * Given the name of a host, returns an array of its IP addresses,
      * based on the configured name service on the system.
@@ -1054,6 +1060,11 @@
      */
     public static InetAddress[] getAllByName(String host)
         throws UnknownHostException {
+        return getAllByName(host, null);
+    }
+
+    private static InetAddress[] getAllByName(String host, InetAddress reqAddr)
+        throws UnknownHostException {
 
         if (host == null || host.length() == 0) {
             InetAddress[] ret = new InetAddress[1];
@@ -1113,7 +1124,7 @@
                 // We were expecting an IPv6 Litteral, but got something else
                 throw new UnknownHostException("["+host+"]");
             }
-        return getAllByName0(host);
+        return getAllByName0(host, reqAddr, true);
     }
 
     /**
@@ -1174,6 +1185,12 @@
      */
     static InetAddress[] getAllByName0 (String host, boolean check)
         throws UnknownHostException  {
+        return getAllByName0 (host, null, check);
+    }
+
+    private static InetAddress[] getAllByName0 (String host, InetAddress reqAddr, boolean check)
+        throws UnknownHostException  {
+
         /* If it gets here it is presumed to be a hostname */
         /* Cache.get can return: null, unknownAddress, or InetAddress[] */
 
@@ -1191,7 +1208,7 @@
 
         /* If no entry in cache, then do the host lookup */
         if (addresses == null) {
-            addresses = getAddressesFromNameService(host);
+            addresses = getAddressesFromNameService(host, reqAddr);
         }
 
         if (addresses == unknown_array)
@@ -1200,7 +1217,7 @@
         return addresses.clone();
     }
 
-    private static InetAddress[] getAddressesFromNameService(String host)
+    private static InetAddress[] getAddressesFromNameService(String host, InetAddress reqAddr)
         throws UnknownHostException
     {
         InetAddress[] addresses = null;
@@ -1256,10 +1273,32 @@
                     }
                 }
 
-                // Cache the addresses.
+                // More to do?
+                if (reqAddr != null && addresses.length > 1 && !addresses[0].equals(reqAddr)) {
+                    // Find it?
+                    int i = 1;
+                    for (; i < addresses.length; i++) {
+                        if (addresses[i].equals(reqAddr)) {
+                            break;
+                        }
+                    }
+                    // Rotate
+                    if (i < addresses.length) {
+                        InetAddress tmp, tmp2 = reqAddr;
+                        for (int j = 0; j < i; j++) {
+                            tmp = addresses[j];
+                            addresses[j] = tmp2;
+                            tmp2 = tmp;
+                        }
+                        addresses[i] = tmp2;
+                    }
+                }
+                // Cache the address.
                 cacheAddresses(host, addresses, success);
+
                 if (!success && ex != null)
                     throw ex;
+
             } finally {
                 // Delete host from the lookupTable and notify
                 // all threads waiting on the lookupTable monitor.
@@ -1393,7 +1432,7 @@
                     InetAddress[] localAddrs;
                     try {
                         localAddrs =
-                            InetAddress.getAddressesFromNameService(local);
+                            InetAddress.getAddressesFromNameService(local, null);
                     } catch (UnknownHostException uhe) {
                         // Rethrow with a more informative error message.
                         UnknownHostException uhe2 =
--- a/jdk/src/share/classes/java/net/Socket.java	Sun May 01 14:22:32 2011 +0800
+++ b/jdk/src/share/classes/java/net/Socket.java	Mon May 02 11:02:58 2011 +0100
@@ -127,11 +127,12 @@
             }
             if (security != null) {
                 if (epoint.isUnresolved())
-                    security.checkConnect(epoint.getHostName(),
-                                          epoint.getPort());
+                    epoint = new InetSocketAddress(epoint.getHostName(), epoint.getPort());
+                if (epoint.isUnresolved())
+                    security.checkConnect(epoint.getHostName(), epoint.getPort());
                 else
                     security.checkConnect(epoint.getAddress().getHostAddress(),
-                                          epoint.getPort());
+                                  epoint.getPort());
             }
             impl = new SocksSocketImpl(p);
             impl.setSocket(this);
--- a/jdk/src/share/classes/java/net/SocketPermission.java	Sun May 01 14:22:32 2011 +0800
+++ b/jdk/src/share/classes/java/net/SocketPermission.java	Mon May 02 11:02:58 2011 +0100
@@ -41,6 +41,7 @@
 import java.io.IOException;
 import sun.net.util.IPAddressUtil;
 import sun.security.util.SecurityConstants;
+import sun.security.util.Debug;
 
 
 /**
@@ -211,13 +212,32 @@
     // port range on host
     private transient int[] portrange;
 
-    // true if the trustProxy system property is set
-    private static boolean trustProxy;
+    private transient boolean defaultDeny = false;
+
+    // true if this SocketPermission represents a hostname
+    // that failed our reverse mapping heuristic test
+    private transient boolean untrusted;
+    private transient boolean trusted;
+
+    // true if the sun.net.trustNameService system property is set
+    private static boolean trustNameService;
+
+    private static Debug debug = null;
+    private static boolean debugInit = false;
 
     static {
         Boolean tmp = java.security.AccessController.doPrivileged(
-                new sun.security.action.GetBooleanAction("trustProxy"));
-        trustProxy = tmp.booleanValue();
+                new sun.security.action.GetBooleanAction("sun.net.trustNameService"));
+        trustNameService = tmp.booleanValue();
+    }
+
+    private static synchronized Debug getDebug()
+    {
+        if (!debugInit) {
+            debug = Debug.getInstance("access");
+            debugInit = true;
+        }
+        return debug;
     }
 
     /**
@@ -263,6 +283,10 @@
         init(getName(), mask);
     }
 
+    private void setDeny() {
+        defaultDeny = true;
+    }
+
     private static String getHost(String host)
     {
         if (host.equals("")) {
@@ -560,6 +584,38 @@
         return mask;
     }
 
+    private boolean isUntrusted()
+        throws UnknownHostException
+    {
+        if (trusted) return false;
+        if (invalid || untrusted) return true;
+        try {
+            if (!trustNameService && (defaultDeny ||
+                sun.net.www.URLConnection.isProxiedHost(hostname))) {
+                if (this.cname == null) {
+                    this.getCanonName();
+                }
+                if (!match(cname, hostname) &&
+                    (defaultDeny || !cname.equals(addresses[0].getHostAddress()))) {
+                    // Last chance
+                    if (!authorized(hostname, addresses[0].getAddress())) {
+                        untrusted = true;
+                        Debug debug = getDebug();
+                        if (debug != null && Debug.isOn("failure")) {
+                            debug.println("socket access restriction: proxied host " + "(" + addresses[0] + ")" + " does not match " + cname + " from reverse lookup");
+                        }
+                        return true;
+                    }
+                }
+                trusted = true;
+            }
+        } catch (UnknownHostException uhe) {
+            invalid = true;
+            throw uhe;
+        }
+        return false;
+    }
+
     /**
      * attempt to get the fully qualified domain name
      *
@@ -567,7 +623,7 @@
     void getCanonName()
         throws UnknownHostException
     {
-        if (cname != null || invalid) return;
+        if (cname != null || invalid || untrusted) return;
 
         // attempt to get the canonical name
 
@@ -593,6 +649,141 @@
         }
     }
 
+    private String cdomain, hdomain;
+
+    private boolean match(String cname, String hname) {
+        String a = cname.toLowerCase();
+        String b = hname.toLowerCase();
+        if (a.startsWith(b)  &&
+            ((a.length() == b.length()) || (a.charAt(b.length()) == '.')))
+            return true;
+        if (cdomain == null) {
+            cdomain = guessRegisteredDomain(a);
+        }
+        if (hdomain == null) {
+            hdomain = guessRegisteredDomain(b);
+        }
+
+        return cdomain.length() != 0 && hdomain.length() != 0
+                        && cdomain.equals(hdomain);
+    }
+
+
+    /* Apart from special cases, this checks for 2 letter TLD
+     * (usually ccTLD) and then for a specific set of common labels
+     * indicating likely 2nd level public suffixes. If both conditions
+     * true then return right most three labels. Otherwise, return
+     * 2 rightmost labels.
+     *
+     * www.sun.com. -> sun.com
+     * www.sun.co.uk -> sun.co.uk
+     * www.sun.com.au -> sun.com.au
+     */
+
+    private String guessRegisteredDomain(String cname) {
+        int dot;
+        dot = cname.lastIndexOf('.');
+        if (dot == -1)
+            return cname;
+        if (dot == 0)
+            return "";
+        if (dot == cname.length() - 1) {
+            cname = cname.substring(0, cname.length() -1);
+            dot = cname.lastIndexOf('.');
+        }
+        if (dot < 1)
+            return "";
+        int second = cname.lastIndexOf('.', dot - 1);
+        if (second == -1)
+            return cname;
+        if (((cname.length() - dot) <= 3) && ((dot - second) <= 4) && second > 0) {
+            if (dot - second == 4) {
+                String s = cname.substring(second + 1, dot);
+                if (!(s.equals("com") || s.equals("org") || s.equals("edu"))) {
+                    return cname.substring(second + 1);
+                }
+            }
+            int third = cname.lastIndexOf('.', second - 1);
+            if (third == -1)
+                return cname.substring(second + 1);
+            else
+                return cname.substring(third + 1);
+        }
+        return cname.substring(second + 1);
+    }
+
+
+    private boolean authorized(String cname, byte[] addr) {
+        if (addr.length == 4)
+            return authorizedIPv4(cname, addr);
+        else if (addr.length == 16)
+            return authorizedIPv6(cname, addr);
+        else
+            return false;
+    }
+
+    private boolean authorizedIPv4(String cname, byte[] addr) {
+            String authHost = "";
+            InetAddress auth;
+
+        try {
+            authHost = "auth." +
+                        (addr[3] & 0xff) + "." + (addr[2] & 0xff) + "." +
+                        (addr[1] & 0xff) + "." + (addr[0] & 0xff) +
+                        ".in-addr.arpa";
+            // Following check seems unnecessary
+            // auth = InetAddress.getAllByName0(authHost, false)[0];
+            authHost = hostname + '.' + authHost;
+            auth = InetAddress.getAllByName0(authHost, false)[0];
+            if (auth.equals(InetAddress.getByAddress(addr))) {
+                return true;
+            }
+            Debug debug = getDebug();
+            if (debug != null && Debug.isOn("failure")) {
+                debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr));
+            }
+        } catch (UnknownHostException uhe) {
+            Debug debug = getDebug();
+            if (debug != null && Debug.isOn("failure")) {
+                debug.println("socket access restriction: forward lookup failed for " + authHost);
+            }
+        }
+        return false;
+    }
+
+    private boolean authorizedIPv6(String cname, byte[] addr) {
+            String authHost = "";
+            InetAddress auth;
+
+        try {
+            StringBuffer sb = new StringBuffer(39);
+
+            for (int i = 15; i >= 0; i--) {
+                sb.append(Integer.toHexString(((addr[i]) & 0x0f)));
+                sb.append('.');
+                sb.append(Integer.toHexString(((addr[i] >> 4) & 0x0f)));
+                sb.append('.');
+            }
+            authHost = "auth." + sb.toString() + "IP6.ARPA";
+            //auth = InetAddress.getAllByName0(authHost, false)[0];
+            authHost = hostname + '.' + authHost;
+            auth = InetAddress.getAllByName0(authHost, false)[0];
+            if (auth.equals(InetAddress.getByAddress(addr)))
+                return true;
+            Debug debug = getDebug();
+            if (debug != null && Debug.isOn("failure")) {
+                debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr));
+            }
+        } catch (UnknownHostException uhe) {
+            Debug debug = getDebug();
+            if (debug != null && Debug.isOn("failure")) {
+                debug.println("socket access restriction: forward lookup failed for " + authHost);
+            }
+        }
+        return false;
+    }
+
+
     /**
      * get IP addresses. Sets invalid to true if we can't get them.
      *
@@ -720,12 +911,7 @@
 
         // return if either one of these NetPerm objects are invalid...
         if (this.invalid || that.invalid) {
-            return (trustProxy ? inProxyWeTrust(that) : false);
-        }
-
-
-        if (this.getName().equalsIgnoreCase(that.getName())) {
-            return true;
+            return compareHostnames(that);
         }
 
         try {
@@ -778,28 +964,29 @@
                 that.getIP();
             }
 
-            for (j = 0; j < this.addresses.length; j++) {
-                for (i=0; i < that.addresses.length; i++) {
-                    if (this.addresses[j].equals(that.addresses[i]))
-                        return true;
+            if (!(that.init_with_ip && this.isUntrusted())) {
+                for (j = 0; j < this.addresses.length; j++) {
+                    for (i=0; i < that.addresses.length; i++) {
+                        if (this.addresses[j].equals(that.addresses[i]))
+                            return true;
+                    }
                 }
+
+                // XXX: if all else fails, compare hostnames?
+                // Do we really want this?
+                if (this.cname == null) {
+                    this.getCanonName();
+                }
+
+                if (that.cname == null) {
+                    that.getCanonName();
+                }
+
+                return (this.cname.equalsIgnoreCase(that.cname));
             }
 
-            // XXX: if all else fails, compare hostnames?
-            // Do we really want this?
-            if (this.cname == null) {
-                this.getCanonName();
-            }
-
-            if (that.cname == null) {
-                that.getCanonName();
-            }
-
-            return (this.cname.equalsIgnoreCase(that.cname));
-
         } catch (UnknownHostException uhe) {
-            if (trustProxy)
-                return inProxyWeTrust(that);
+            return compareHostnames(that);
         }
 
         // make sure the first thing that is done here is to return
@@ -808,19 +995,23 @@
         return false;
     }
 
-    private boolean inProxyWeTrust(SocketPermission that) {
-        // if we trust the proxy, we see if the original names/IPs passed
-        // in were equal.
+    private boolean compareHostnames(SocketPermission that) {
+        // we see if the original names/IPs passed in were equal.
 
         String thisHost = hostname;
         String thatHost = that.hostname;
 
-        if (thisHost == null)
+        if (thisHost == null) {
             return false;
-        else
+        } else if (this.wildcard) {
+            final int cnameLength = this.cname.length();
+            return thatHost.regionMatches(true,
+                (thatHost.length() - cnameLength), this.cname, 0, cnameLength);
+        } else {
             return thisHost.equalsIgnoreCase(thatHost);
+        }
+    }
 
-    }
     /**
      * Checks two SocketPermission objects for equality.
      * <P>
--- a/jdk/src/share/classes/sun/net/www/URLConnection.java	Sun May 01 14:22:32 2011 +0800
+++ b/jdk/src/share/classes/sun/net/www/URLConnection.java	Mon May 02 11:02:58 2011 +0100
@@ -238,4 +238,14 @@
     public void close() {
         url = null;
     }
+
+    private static HashMap<String,Void> proxiedHosts = new HashMap<>();
+
+    public synchronized static void setProxiedHost(String host) {
+        proxiedHosts.put(host.toLowerCase(), null);
+    }
+
+    public synchronized static boolean isProxiedHost(String host) {
+        return proxiedHosts.containsKey(host.toLowerCase());
+    }
 }
--- a/jdk/src/share/classes/sun/net/www/http/HttpClient.java	Sun May 01 14:22:32 2011 +0800
+++ b/jdk/src/share/classes/sun/net/www/http/HttpClient.java	Mon May 02 11:02:58 2011 +0100
@@ -301,7 +301,11 @@
         } else {
             SecurityManager security = System.getSecurityManager();
             if (security != null) {
-                security.checkConnect(url.getHost(), url.getPort());
+                if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) {
+                    security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort());
+                } else {
+                    security.checkConnect(url.getHost(), url.getPort());
+                }
             }
             ret.url = url;
         }
@@ -457,11 +461,11 @@
     protected synchronized void openServer() throws IOException {
 
         SecurityManager security = System.getSecurityManager();
-        if (security != null) {
-            security.checkConnect(host, port);
-        }
 
         if (keepingAlive) { // already opened
+            if (security != null) {
+                security.checkConnect(host, port);
+            }
             return;
         }
 
@@ -469,11 +473,19 @@
             url.getProtocol().equals("https") ) {
 
             if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {
+                sun.net.www.URLConnection.setProxiedHost(host);
+                if (security != null) {
+                    security.checkConnect(host,port);
+                }
                 privilegedOpenServer((InetSocketAddress) proxy.address());
                 usingProxy = true;
                 return;
             } else {
                 // make direct connection
+                if (security != null) {
+                    // redundant?
+                    security.checkConnect(host, port);
+                }
                 openServer(host, port);
                 usingProxy = false;
                 return;
@@ -484,11 +496,19 @@
              * ftp url.
              */
             if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {
+                sun.net.www.URLConnection.setProxiedHost(host);
+                if (security != null) {
+                    security.checkConnect(host,port);
+                }
                 privilegedOpenServer((InetSocketAddress) proxy.address());
                 usingProxy = true;
                 return;
             } else {
                 // make direct connection
+                if (security != null) {
+                    // redundant?
+                    security.checkConnect(host, port);
+                }
                 super.openServer(host, port);
                 usingProxy = false;
                 return;