6682516: SPNEGO_HTTP_AUTH/WWW_KRB and SPNEGO_HTTP_AUTH/WWW_SPNEGO failed on all non-windows platforms

Reviewed-by: xuelei
diff --git a/jdk/src/share/classes/sun/security/krb5/PrincipalName.java b/jdk/src/share/classes/sun/security/krb5/PrincipalName.java
index 0e6b328..3761ffb 100644
--- a/jdk/src/share/classes/sun/security/krb5/PrincipalName.java
+++ b/jdk/src/share/classes/sun/security/krb5/PrincipalName.java
@@ -1,5 +1,5 @@
 /*
- * Portions Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Portions Copyright 2000-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -36,8 +36,6 @@
 import java.net.*;
 import java.util.Vector;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
 import java.math.BigInteger;
 import sun.security.krb5.internal.ccache.CCacheOutputStream;
 
@@ -383,19 +381,26 @@
         switch (type) {
         case KRB_NT_SRV_HST:
             if (nameParts.length >= 2) {
+                String hostName = nameParts[1];
                 try {
-                    // Canonicalize the hostname as per the
-                    // RFC4120 Section 6.2.1 and
-                    // RFC1964 Section 2.1.2
-                    // we assume internet domain names
-                    String hostName =
-                        (InetAddress.getByName(nameParts[1])).
-                        getCanonicalHostName();
-                    nameParts[1] = hostName.toLowerCase();
+                    // RFC4120 does not recommend canonicalizing a hostname.
+                    // However, for compatibility reason, we will try
+                    // canonicalize it and see if the output looks better.
+
+                    String canonicalized = (InetAddress.getByName(hostName)).
+                            getCanonicalHostName();
+
+                    // Looks if canonicalized is a longer format of hostName,
+                    // we accept cases like
+                    //     bunny -> bunny.rabbit.hole
+                    if (canonicalized.toLowerCase()
+                            .startsWith(hostName.toLowerCase()+".")) {
+                        hostName = canonicalized;
+                    }
                 } catch (UnknownHostException e) {
-                    // no canonicalization, just convert to lowercase
-                    nameParts[1] = nameParts[1].toLowerCase();
+                    // no canonicalization, use old
                 }
+                nameParts[1] = hostName.toLowerCase();
             }
             nameStrings = nameParts;
             nameType = type;
diff --git a/jdk/test/sun/security/krb5/canonicalize/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor b/jdk/test/sun/security/krb5/canonicalize/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
new file mode 100644
index 0000000..345e6ae
--- /dev/null
+++ b/jdk/test/sun/security/krb5/canonicalize/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
@@ -0,0 +1 @@
+Test
diff --git a/jdk/test/sun/security/krb5/canonicalize/Test.java b/jdk/test/sun/security/krb5/canonicalize/Test.java
new file mode 100644
index 0000000..e4fb4f6
--- /dev/null
+++ b/jdk/test/sun/security/krb5/canonicalize/Test.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+/*
+ * @test
+ * @bug 6682516
+ * @summary SPNEGO_HTTP_AUTH/WWW_KRB and SPNEGO_HTTP_AUTH/WWW_SPNEGO failed on all non-windows platforms
+ * @run main/othervm -Dsun.net.spi.nameservice.provider.1=ns,mock -Djava.security.krb5.conf=krb5.conf Test
+ */
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import sun.net.spi.nameservice.NameService;
+import sun.net.spi.nameservice.NameServiceDescriptor;
+import sun.security.krb5.PrincipalName;
+
+public class Test implements NameServiceDescriptor {
+    public static void main(String[] args) throws Exception {
+        // This config file is generated using Kerberos.app on a Mac
+        System.setProperty("java.security.krb5.realm", "THIS.REALM");
+        System.setProperty("java.security.krb5.kdc", "localhost");
+
+        // add using canonicalized name
+        check("c1", "c1.this.domain");
+        check("c1.this", "c1.this.domain");
+        check("c1.this.domain", "c1.this.domain");
+
+        // canonicalized name goes IP, reject
+        check("c2", "c2");
+
+        // canonicalized name goes strange, reject
+        check("c3", "c3");
+
+        // unsupported
+        check("c4", "c4");
+    }
+
+    static void check(String input, String output) throws Exception {
+        System.out.println(input + " -> " + output);
+        PrincipalName pn = new PrincipalName("host/"+input,
+                PrincipalName.KRB_NT_SRV_HST);
+        if (!pn.getNameStrings()[1].equals(output)) {
+            throw new Exception("Output is " + pn);
+        }
+    }
+
+    @Override
+    public NameService createNameService() throws Exception {
+        NameService ns = new NameService() {
+            @Override
+            public InetAddress[] lookupAllHostAddr(String host)
+                    throws UnknownHostException {
+                // All c<n>.* goes to 127.0.0.n
+                int i = Integer.valueOf(host.split("\\.")[0].substring(1));
+                return new InetAddress[]{
+                    InetAddress.getByAddress(host, new byte[]{127,0,0,(byte)i})
+                };
+            }
+            @Override
+            public String getHostByAddr(byte[] addr)
+                    throws UnknownHostException {
+                int i = addr[3];
+                switch (i) {
+                    case 1: return "c1.this.domain";        // Good
+                    case 2: return "127.0.0.2";             // Only IP
+                    case 3: return "d3.this.domain";        // name change
+                    default:
+                        throw new UnknownHostException();
+                }
+            }
+        };
+        return ns;
+    }
+
+    @Override
+    public String getProviderName() {
+        return "mock";
+    }
+
+    @Override
+    public String getType() {
+        return "ns";
+    }
+}