8218873: Improve JSSE endpoint checking
authorxuelei
Thu, 11 Apr 2019 17:58:18 -0700
changeset 55713 6cd74f29752c
parent 55712 107c8ea4f7c8
child 55714 e17ec6bc670a
8218873: Improve JSSE endpoint checking Reviewed-by: mullan, ahgross, rhalade
src/java.base/share/classes/sun/security/util/HostnameChecker.java
--- a/src/java.base/share/classes/sun/security/util/HostnameChecker.java	Wed Apr 10 14:43:13 2019 -0700
+++ b/src/java.base/share/classes/sun/security/util/HostnameChecker.java	Thu Apr 11 17:58:18 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, Oracle and/or its affiliates. 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
@@ -260,28 +260,35 @@
      * The matching is performed as per RFC 2818 rules for TLS and
      * RFC 2830 rules for LDAP.<p>
      *
-     * The <code>name</code> parameter should represent a DNS name.
-     * The <code>template</code> parameter
-     * may contain the wildcard character *
+     * The <code>name</code> parameter should represent a DNS name.  The
+     * <code>template</code> parameter may contain the wildcard character '*'.
      */
     private boolean isMatched(String name, String template,
                               boolean chainsToPublicCA) {
 
         // Normalize to Unicode, because PSL is in Unicode.
-        name = IDN.toUnicode(IDN.toASCII(name));
-        template = IDN.toUnicode(IDN.toASCII(template));
+        try {
+            name = IDN.toUnicode(IDN.toASCII(name));
+            template = IDN.toUnicode(IDN.toASCII(template));
+        } catch (RuntimeException re) {
+            if (SSLLogger.isOn) {
+                SSLLogger.fine("Failed to normalize to Unicode: " + re);
+            }
 
-        if (hasIllegalWildcard(name, template, chainsToPublicCA)) {
+            return false;
+        }
+
+        if (hasIllegalWildcard(template, chainsToPublicCA)) {
             return false;
         }
 
         // check the validity of the domain name template.
         try {
-            // Replacing wildcard character '*' with 'x' so as to check
+            // Replacing wildcard character '*' with 'z' so as to check
             // the domain name template validity.
             //
             // Using the checking implemented in SNIHostName
-            new SNIHostName(template.replace('*', 'x'));
+            new SNIHostName(template.replace('*', 'z'));
         } catch (IllegalArgumentException iae) {
             // It would be nice to add debug log if not matching.
             return false;
@@ -299,8 +306,8 @@
     /**
      * Returns true if the template contains an illegal wildcard character.
      */
-    private static boolean hasIllegalWildcard(String domain, String template,
-                                              boolean chainsToPublicCA) {
+    private static boolean hasIllegalWildcard(
+            String template, boolean chainsToPublicCA) {
         // not ok if it is a single wildcard character or "*."
         if (template.equals("*") || template.equals("*.")) {
             if (SSLLogger.isOn) {
@@ -331,25 +338,29 @@
             return true;
         }
 
-        // If the wildcarded domain is a top-level domain under which names
-        // can be registered, then a wildcard is not allowed.
-
         if (!chainsToPublicCA) {
             return false; // skip check for non-public certificates
         }
-        Optional<RegisteredDomain> rd = RegisteredDomain.from(domain)
-                .filter(d -> d.type() == RegisteredDomain.Type.ICANN);
 
-        if (rd.isPresent()) {
-            String wDomain = afterWildcard.substring(firstDotIndex + 1);
-            if (rd.get().publicSuffix().equalsIgnoreCase(wDomain)) {
-                if (SSLLogger.isOn) {
-                    SSLLogger.fine(
-                        "Certificate domain name has illegal " +
-                        "wildcard for public suffix: " + template);
-                }
-                return true;
+        // If the wildcarded domain is a top-level domain under which names
+        // can be registered, then a wildcard is not allowed.
+        String wildcardedDomain = afterWildcard.substring(firstDotIndex + 1);
+        String templateDomainSuffix =
+                RegisteredDomain.from("z." + wildcardedDomain)
+                    .filter(d -> d.type() == RegisteredDomain.Type.ICANN)
+                    .map(RegisteredDomain::publicSuffix).orElse(null);
+        if (templateDomainSuffix == null) {
+            return false;   // skip check if not known public suffix
+        }
+
+        // Is it a top-level domain?
+        if (wildcardedDomain.equalsIgnoreCase(templateDomainSuffix)) {
+            if (SSLLogger.isOn) {
+                SSLLogger.fine(
+                    "Certificate domain name has illegal " +
+                    "wildcard for top-level public suffix: " + template);
             }
+            return true;
         }
 
         return false;