8038893: Recertify certificate matching

Reviewed-by: xuelei, jdn, erikj, asmotrak
This commit is contained in:
Sean Mullan 2017-03-20 07:38:52 -04:00
parent 240fd78fc1
commit 2cef018734
7 changed files with 312 additions and 98 deletions

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
# #
# This code is free software; you can redistribute it and/or modify it # This code is free software; you can redistribute it and/or modify it
@ -30,11 +30,19 @@ include MakeBase.gmk
include JavaCompilation.gmk include JavaCompilation.gmk
include SetupJavaCompilers.gmk include SetupJavaCompilers.gmk
$(eval $(call IncludeCustomExtension, jdk, CompileTools.gmk))
################################################################################ ################################################################################
# Use += to be able to add to this from a custom extension
BUILD_TOOLS_SRC_DIRS += \
$(JDK_TOPDIR)/make/src/classes \
$(BUILDTOOLS_OUTPUTDIR)/interim_cldrconverter_classes \
#
$(eval $(call SetupJavaCompilation,BUILD_TOOLS_JDK, \ $(eval $(call SetupJavaCompilation,BUILD_TOOLS_JDK, \
SETUP := GENERATE_OLDBYTECODE, \ SETUP := GENERATE_OLDBYTECODE, \
SRC := $(JDK_TOPDIR)/make/src/classes $(BUILDTOOLS_OUTPUTDIR)/interim_cldrconverter_classes, \ SRC := $(BUILD_TOOLS_SRC_DIRS), \
EXCLUDES := build/tools/deps \ EXCLUDES := build/tools/deps \
build/tools/jigsaw, \ build/tools/jigsaw, \
BIN := $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes)) BIN := $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes))

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
# #
# This code is free software; you can redistribute it and/or modify it # This code is free software; you can redistribute it and/or modify it
@ -28,6 +28,9 @@ _TOOLS_GMK := 1
include JavaCompilation.gmk include JavaCompilation.gmk
# Hook to include the corresponding custom file, if present.
$(eval $(call IncludeCustomExtension, jdk, Tools.gmk))
################################################################################ ################################################################################
# To avoid reevaluating the compilation setup for the tools each time this file # To avoid reevaluating the compilation setup for the tools each time this file
# is included, the actual compilation is handled by CompileTools.gmk. The # is included, the actual compilation is handled by CompileTools.gmk. The

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -44,8 +44,8 @@ import java.util.StringJoiner;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListMap;
import sun.net.util.IPAddressUtil; import sun.net.util.IPAddressUtil;
import sun.net.RegisteredDomain;
import sun.net.PortConfig; import sun.net.PortConfig;
import sun.security.util.RegisteredDomain;
import sun.security.util.SecurityConstants; import sun.security.util.SecurityConstants;
import sun.security.util.Debug; import sun.security.util.Debug;
@ -678,13 +678,18 @@ public final class SocketPermission extends Permission
String a = cname.toLowerCase(); String a = cname.toLowerCase();
String b = hname.toLowerCase(); String b = hname.toLowerCase();
if (a.startsWith(b) && if (a.startsWith(b) &&
((a.length() == b.length()) || (a.charAt(b.length()) == '.'))) ((a.length() == b.length()) || (a.charAt(b.length()) == '.'))) {
return true; return true;
}
if (cdomain == null) { if (cdomain == null) {
cdomain = RegisteredDomain.getRegisteredDomain(a); cdomain = RegisteredDomain.from(a)
.map(RegisteredDomain::name)
.orElse(a);
} }
if (hdomain == null) { if (hdomain == null) {
hdomain = RegisteredDomain.getRegisteredDomain(b); hdomain = RegisteredDomain.from(b)
.map(RegisteredDomain::name)
.orElse(b);
} }
return cdomain.length() != 0 && hdomain.length() != 0 return cdomain.length() != 0 && hdomain.length() != 0

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -32,6 +32,10 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
/* /*
* WARNING: This class may contain out-of-date information. It should be
* updated or replaced with an appropriate implementation. See
* sun.security.util.RegisteredDomain for more information.
*
* The naming tables listed below were gathered from publicly available data such as * The naming tables listed below were gathered from publicly available data such as
* the subdomain registration websites listed for each top-level domain by the Internet * the subdomain registration websites listed for each top-level domain by the Internet
* Assigned Numbers Authority and the website of the Internet Corporation for Assigned Names * Assigned Numbers Authority and the website of the Internet Corporation for Assigned Names
@ -696,6 +700,36 @@ static {
top3Map.put("tr", new HashSet<String>(Arrays.asList("gov.nc.tr"))); top3Map.put("tr", new HashSet<String>(Arrays.asList("gov.nc.tr")));
} }
/**
* Returns a {@code sun.security.util.RegisteredDomain} representing the
* registered part of the specified domain.
*
* @param domain the domain name
* @return a {@code sun.security.util.RegisteredDomain} or null
* if the domain is unknown or not registerable
* @throws NullPointerException if domain is null
*/
public static sun.security.util.RegisteredDomain registeredDomain(String domain) {
String name = getRegisteredDomain(domain);
if (name.equals(domain)) {
return null;
}
return new sun.security.util.RegisteredDomain() {
private String rname = name;
@Override
public String name() {
return rname;
}
@Override
public sun.security.util.RegisteredDomain.Type type() {
return sun.security.util.RegisteredDomain.Type.ICANN;
}
@Override
public String publicSuffix() {
return rname.substring(rname.indexOf(".") + 1);
}
};
}
/* /*
* Return the registered part of a qualified domain * Return the registered part of a qualified domain

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -35,6 +35,7 @@ import java.security.cert.*;
import javax.net.ssl.*; import javax.net.ssl.*;
import sun.security.validator.*; import sun.security.validator.*;
import sun.security.util.AnchorCertificates;
import sun.security.util.HostnameChecker; import sun.security.util.HostnameChecker;
/** /**
@ -186,13 +187,11 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
return v; return v;
} }
private void checkTrusted(X509Certificate[] chain, String authType, private void checkTrusted(X509Certificate[] chain, String authType,
Socket socket, boolean isClient) throws CertificateException { Socket socket, boolean isClient) throws CertificateException {
Validator v = checkTrustedInit(chain, authType, isClient); Validator v = checkTrustedInit(chain, authType, isClient);
AlgorithmConstraints constraints = null; X509Certificate[] trustedChain = null;
List<byte[]> responseList = Collections.emptyList();
if ((socket != null) && socket.isConnected() && if ((socket != null) && socket.isConnected() &&
(socket instanceof SSLSocket)) { (socket instanceof SSLSocket)) {
@ -202,48 +201,46 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
throw new CertificateException("No handshake session"); throw new CertificateException("No handshake session");
} }
// check endpoint identity
String identityAlg = sslSocket.getSSLParameters().
getEndpointIdentificationAlgorithm();
if (identityAlg != null && identityAlg.length() != 0) {
checkIdentity(session, chain[0], identityAlg, isClient,
getRequestedServerNames(socket));
}
// create the algorithm constraints // create the algorithm constraints
ProtocolVersion protocolVersion = ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol()); ProtocolVersion.valueOf(session.getProtocol());
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { boolean isExtSession = (session instanceof ExtendedSSLSession);
if (session instanceof ExtendedSSLSession) { AlgorithmConstraints constraints = null;
ExtendedSSLSession extSession = if (protocolVersion.v >= ProtocolVersion.TLS12.v && isExtSession) {
(ExtendedSSLSession)session; ExtendedSSLSession extSession = (ExtendedSSLSession)session;
String[] localSupportedSignAlgs = String[] localSupportedSignAlgs =
extSession.getLocalSupportedSignatureAlgorithms(); extSession.getLocalSupportedSignatureAlgorithms();
constraints = new SSLAlgorithmConstraints( constraints = new SSLAlgorithmConstraints(
sslSocket, localSupportedSignAlgs, false); sslSocket, localSupportedSignAlgs, false);
} else {
constraints =
new SSLAlgorithmConstraints(sslSocket, false);
}
} else { } else {
constraints = new SSLAlgorithmConstraints(sslSocket, false); constraints = new SSLAlgorithmConstraints(sslSocket, false);
} }
// Grab any stapled OCSP responses for use in validation // Grab any stapled OCSP responses for use in validation
if (session instanceof ExtendedSSLSession) { List<byte[]> responseList = Collections.emptyList();
if (!isClient && isExtSession) {
responseList = responseList =
((ExtendedSSLSession)session).getStatusResponses(); ((ExtendedSSLSession)session).getStatusResponses();
} }
} trustedChain = validate(v, chain, responseList,
constraints, isClient ? null : authType);
X509Certificate[] trustedChain = null; // check if EE certificate chains to a public root CA (as
if (isClient) { // pre-installed in cacerts)
trustedChain = validate(v, chain, Collections.emptyList(), boolean chainsToPublicCA =
constraints, null); AnchorCertificates.contains(trustedChain[trustedChain.length-1]);
// check endpoint identity
String identityAlg = sslSocket.getSSLParameters().
getEndpointIdentificationAlgorithm();
if (identityAlg != null && identityAlg.length() != 0) {
checkIdentity(session, trustedChain[0], identityAlg, isClient,
getRequestedServerNames(socket), chainsToPublicCA);
}
} else { } else {
trustedChain = validate(v, chain, responseList, constraints, trustedChain = validate(v, chain, Collections.emptyList(),
authType); null, isClient ? null : authType);
} }
if (debug != null && Debug.isOn("trustmanager")) { if (debug != null && Debug.isOn("trustmanager")) {
System.out.println("Found trusted certificate:"); System.out.println("Found trusted certificate:");
@ -255,56 +252,53 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
SSLEngine engine, boolean isClient) throws CertificateException { SSLEngine engine, boolean isClient) throws CertificateException {
Validator v = checkTrustedInit(chain, authType, isClient); Validator v = checkTrustedInit(chain, authType, isClient);
AlgorithmConstraints constraints = null; X509Certificate[] trustedChain = null;
List<byte[]> responseList = Collections.emptyList();
if (engine != null) { if (engine != null) {
SSLSession session = engine.getHandshakeSession(); SSLSession session = engine.getHandshakeSession();
if (session == null) { if (session == null) {
throw new CertificateException("No handshake session"); throw new CertificateException("No handshake session");
} }
// check endpoint identity
String identityAlg = engine.getSSLParameters().
getEndpointIdentificationAlgorithm();
if (identityAlg != null && identityAlg.length() != 0) {
checkIdentity(session, chain[0], identityAlg, isClient,
getRequestedServerNames(engine));
}
// create the algorithm constraints // create the algorithm constraints
ProtocolVersion protocolVersion = ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol()); ProtocolVersion.valueOf(session.getProtocol());
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { boolean isExtSession = (session instanceof ExtendedSSLSession);
if (session instanceof ExtendedSSLSession) { AlgorithmConstraints constraints = null;
ExtendedSSLSession extSession = if (protocolVersion.v >= ProtocolVersion.TLS12.v && isExtSession) {
(ExtendedSSLSession)session; ExtendedSSLSession extSession = (ExtendedSSLSession)session;
String[] localSupportedSignAlgs = String[] localSupportedSignAlgs =
extSession.getLocalSupportedSignatureAlgorithms(); extSession.getLocalSupportedSignatureAlgorithms();
constraints = new SSLAlgorithmConstraints( constraints = new SSLAlgorithmConstraints(
engine, localSupportedSignAlgs, false); engine, localSupportedSignAlgs, false);
} else {
constraints =
new SSLAlgorithmConstraints(engine, false);
}
} else { } else {
constraints = new SSLAlgorithmConstraints(engine, false); constraints = new SSLAlgorithmConstraints(engine, false);
} }
// Grab any stapled OCSP responses for use in validation // Grab any stapled OCSP responses for use in validation
if (session instanceof ExtendedSSLSession) { List<byte[]> responseList = Collections.emptyList();
if (!isClient && isExtSession) {
responseList = responseList =
((ExtendedSSLSession)session).getStatusResponses(); ((ExtendedSSLSession)session).getStatusResponses();
} }
} trustedChain = validate(v, chain, responseList,
constraints, isClient ? null : authType);
X509Certificate[] trustedChain = null; // check if EE certificate chains to a public root CA (as
if (isClient) { // pre-installed in cacerts)
trustedChain = validate(v, chain, Collections.emptyList(), boolean chainsToPublicCA =
constraints, null); AnchorCertificates.contains(trustedChain[trustedChain.length-1]);
// check endpoint identity
String identityAlg = engine.getSSLParameters().
getEndpointIdentificationAlgorithm();
if (identityAlg != null && identityAlg.length() != 0) {
checkIdentity(session, trustedChain[0], identityAlg, isClient,
getRequestedServerNames(engine), chainsToPublicCA);
}
} else { } else {
trustedChain = validate(v, chain, responseList, constraints, trustedChain = validate(v, chain, Collections.emptyList(),
authType); null, isClient ? null : authType);
} }
if (debug != null && Debug.isOn("trustmanager")) { if (debug != null && Debug.isOn("trustmanager")) {
System.out.println("Found trusted certificate:"); System.out.println("Found trusted certificate:");
@ -437,7 +431,8 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
X509Certificate cert, X509Certificate cert,
String algorithm, String algorithm,
boolean isClient, boolean isClient,
List<SNIServerName> sniNames) throws CertificateException { List<SNIServerName> sniNames,
boolean chainsToPublicCA) throws CertificateException {
boolean identifiable = false; boolean identifiable = false;
String peerHost = session.getPeerHost(); String peerHost = session.getPeerHost();
@ -445,7 +440,7 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
String hostname = getHostNameInSNI(sniNames); String hostname = getHostNameInSNI(sniNames);
if (hostname != null) { if (hostname != null) {
try { try {
checkIdentity(hostname, cert, algorithm); checkIdentity(hostname, cert, algorithm, chainsToPublicCA);
identifiable = true; identifiable = true;
} catch (CertificateException ce) { } catch (CertificateException ce) {
if (hostname.equalsIgnoreCase(peerHost)) { if (hostname.equalsIgnoreCase(peerHost)) {
@ -458,7 +453,7 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
} }
if (!identifiable) { if (!identifiable) {
checkIdentity(peerHost, cert, algorithm); checkIdentity(peerHost, cert, algorithm, chainsToPublicCA);
} }
} }
@ -469,6 +464,12 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
*/ */
static void checkIdentity(String hostname, X509Certificate cert, static void checkIdentity(String hostname, X509Certificate cert,
String algorithm) throws CertificateException { String algorithm) throws CertificateException {
checkIdentity(hostname, cert, algorithm, false);
}
private static void checkIdentity(String hostname, X509Certificate cert,
String algorithm, boolean chainsToPublicCA)
throws CertificateException {
if (algorithm != null && algorithm.length() != 0) { if (algorithm != null && algorithm.length() != 0) {
// if IPv6 strip off the "[]" // if IPv6 strip off the "[]"
if ((hostname != null) && hostname.startsWith("[") && if ((hostname != null) && hostname.startsWith("[") &&
@ -478,11 +479,11 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
if (algorithm.equalsIgnoreCase("HTTPS")) { if (algorithm.equalsIgnoreCase("HTTPS")) {
HostnameChecker.getInstance(HostnameChecker.TYPE_TLS).match( HostnameChecker.getInstance(HostnameChecker.TYPE_TLS).match(
hostname, cert); hostname, cert, chainsToPublicCA);
} else if (algorithm.equalsIgnoreCase("LDAP") || } else if (algorithm.equalsIgnoreCase("LDAP") ||
algorithm.equalsIgnoreCase("LDAPS")) { algorithm.equalsIgnoreCase("LDAPS")) {
HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP).match( HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP).match(
hostname, cert); hostname, cert, chainsToPublicCA);
} else { } else {
throw new CertificateException( throw new CertificateException(
"Unknown identification algorithm: " + algorithm); "Unknown identification algorithm: " + algorithm);

View File

@ -28,17 +28,15 @@ package sun.security.util;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.*;
import java.security.Principal; import java.security.Principal;
import java.security.cert.*; import java.security.cert.*;
import java.util.*;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import sun.security.ssl.ClientKeyExchangeService;
import sun.security.x509.X500Name;
import sun.net.util.IPAddressUtil; import sun.net.util.IPAddressUtil;
import sun.security.ssl.ClientKeyExchangeService;
import sun.security.ssl.Debug;
import sun.security.x509.X500Name;
/** /**
* Class to check hostnames against the names specified in a certificate as * Class to check hostnames against the names specified in a certificate as
@ -61,6 +59,8 @@ public class HostnameChecker {
private static final int ALTNAME_DNS = 2; private static final int ALTNAME_DNS = 2;
private static final int ALTNAME_IP = 7; private static final int ALTNAME_IP = 7;
private static final Debug debug = Debug.getInstance("ssl");
// the algorithm to follow to perform the check. Currently unused. // the algorithm to follow to perform the check. Currently unused.
private final byte checkType; private final byte checkType;
@ -84,18 +84,27 @@ public class HostnameChecker {
/** /**
* Perform the check. * Perform the check.
* *
* @exception CertificateException if the name does not match any of * @param expectedName the expected host name or ip address
* the names specified in the certificate * @param cert the certificate to check against
* @param chainsToPublicCA true if the certificate chains to a public
* root CA (as pre-installed in the cacerts file)
* @throws CertificateException if the name does not match any of
* the names specified in the certificate
*/ */
public void match(String expectedName, X509Certificate cert) public void match(String expectedName, X509Certificate cert,
throws CertificateException { boolean chainsToPublicCA) throws CertificateException {
if (isIpAddress(expectedName)) { if (isIpAddress(expectedName)) {
matchIP(expectedName, cert); matchIP(expectedName, cert);
} else { } else {
matchDNS(expectedName, cert); matchDNS(expectedName, cert, chainsToPublicCA);
} }
} }
public void match(String expectedName, X509Certificate cert)
throws CertificateException {
match(expectedName, cert, false);
}
/** /**
* Perform the check for Kerberos. * Perform the check for Kerberos.
*/ */
@ -185,20 +194,21 @@ public class HostnameChecker {
* Certification Authorities are encouraged to use the dNSName instead. * Certification Authorities are encouraged to use the dNSName instead.
* *
* Matching is performed using the matching rules specified by * Matching is performed using the matching rules specified by
* [RFC2459]. If more than one identity of a given type is present in * [RFC5280]. If more than one identity of a given type is present in
* the certificate (e.g., more than one dNSName name, a match in any one * the certificate (e.g., more than one dNSName name, a match in any one
* of the set is considered acceptable.) * of the set is considered acceptable.)
*/ */
private void matchDNS(String expectedName, X509Certificate cert) private void matchDNS(String expectedName, X509Certificate cert,
boolean chainsToPublicCA)
throws CertificateException { throws CertificateException {
Collection<List<?>> subjAltNames = cert.getSubjectAlternativeNames(); Collection<List<?>> subjAltNames = cert.getSubjectAlternativeNames();
if (subjAltNames != null) { if (subjAltNames != null) {
boolean foundDNS = false; boolean foundDNS = false;
for ( List<?> next : subjAltNames) { for (List<?> next : subjAltNames) {
if (((Integer)next.get(0)).intValue() == ALTNAME_DNS) { if (((Integer)next.get(0)).intValue() == ALTNAME_DNS) {
foundDNS = true; foundDNS = true;
String dnsName = (String)next.get(1); String dnsName = (String)next.get(1);
if (isMatched(expectedName, dnsName)) { if (isMatched(expectedName, dnsName, chainsToPublicCA)) {
return; return;
} }
} }
@ -215,7 +225,8 @@ public class HostnameChecker {
(X500Name.commonName_oid); (X500Name.commonName_oid);
if (derValue != null) { if (derValue != null) {
try { try {
if (isMatched(expectedName, derValue.getAsString())) { if (isMatched(expectedName, derValue.getAsString(),
chainsToPublicCA)) {
return; return;
} }
} catch (IOException e) { } catch (IOException e) {
@ -261,7 +272,11 @@ public class HostnameChecker {
* The <code>template</code> parameter * The <code>template</code> parameter
* may contain the wildcard character * * may contain the wildcard character *
*/ */
private boolean isMatched(String name, String template) { private boolean isMatched(String name, String template,
boolean chainsToPublicCA) {
if (hasIllegalWildcard(name, template, chainsToPublicCA)) {
return false;
}
if (checkType == TYPE_TLS) { if (checkType == TYPE_TLS) {
return matchAllWildcards(name, template); return matchAllWildcards(name, template);
} else if (checkType == TYPE_LDAP) { } else if (checkType == TYPE_LDAP) {
@ -271,6 +286,61 @@ public class HostnameChecker {
} }
} }
/**
* Returns true if the template contains an illegal wildcard character.
*/
private static boolean hasIllegalWildcard(String domain, String template,
boolean chainsToPublicCA) {
// not ok if it is a single wildcard character or "*."
if (template.equals("*") || template.equals("*.")) {
if (debug != null) {
debug.println("Certificate domain name has illegal single " +
"wildcard character: " + template);
}
return true;
}
int lastWildcardIndex = template.lastIndexOf("*");
// ok if it has no wildcard character
if (lastWildcardIndex == -1) {
return false;
}
String afterWildcard = template.substring(lastWildcardIndex);
int firstDotIndex = afterWildcard.indexOf(".");
// not ok if there is no dot after wildcard (ex: "*com")
if (firstDotIndex == -1) {
if (debug != null) {
debug.println("Certificate domain name has illegal wildcard, " +
"no dot after wildcard character: " + template);
}
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 (debug != null) {
debug.println("Certificate domain name has illegal " +
"wildcard for public suffix: " + template);
}
return true;
}
}
return false;
}
/** /**
* Returns true if name matches against template.<p> * Returns true if name matches against template.<p>
@ -317,9 +387,9 @@ public class HostnameChecker {
name = name.toLowerCase(Locale.ENGLISH); name = name.toLowerCase(Locale.ENGLISH);
template = template.toLowerCase(Locale.ENGLISH); template = template.toLowerCase(Locale.ENGLISH);
// Retreive leftmost component // Retrieve leftmost component
int templateIdx = template.indexOf('.'); int templateIdx = template.indexOf(".");
int nameIdx = name.indexOf('.'); int nameIdx = name.indexOf(".");
if (templateIdx == -1) if (templateIdx == -1)
templateIdx = template.length(); templateIdx = template.length();
@ -344,7 +414,7 @@ public class HostnameChecker {
*/ */
private static boolean matchWildCards(String name, String template) { private static boolean matchWildCards(String name, String template) {
int wildcardIdx = template.indexOf('*'); int wildcardIdx = template.indexOf("*");
if (wildcardIdx == -1) if (wildcardIdx == -1)
return name.equals(template); return name.equals(template);
@ -367,7 +437,7 @@ public class HostnameChecker {
// update the match scope // update the match scope
name = name.substring(beforeStartIdx + beforeWildcard.length()); name = name.substring(beforeStartIdx + beforeWildcard.length());
wildcardIdx = afterWildcard.indexOf('*'); wildcardIdx = afterWildcard.indexOf("*");
} }
return name.endsWith(afterWildcard); return name.endsWith(afterWildcard);
} }

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2017, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.util;
import java.util.Optional;
/**
* A domain that is registered under a "public suffix". The public suffix is
* a top-level domain under which names can be registered. For example,
* "com" and "co.uk" are public suffixes, and "example.com" and "example.co.uk"
* are registered domains.
* <p>
* The primary purpose of this class is to determine if domains are safe to
* use in various use-cases.
*/
public interface RegisteredDomain {
public enum Type {
/**
* An ICANN registered domain.
*/
ICANN,
/**
* A private registered domain.
*/
PRIVATE
}
/**
* Returns the name of the registered domain.
*
* @return the name of the registered domain
*/
String name();
/**
* Returns the type of the registered domain.
*
* @return the type of the registered domain
*/
Type type();
/**
* Returns the public suffix of the registered domain.
*
* @return the public suffix of the registered domain
*/
String publicSuffix();
/**
* Returns an {@code Optional<RegisteredDomain>} representing the
* registered part of the specified domain.
*
* {@implNote}
* The default implementation is based on the legacy
* {@code sun.net.RegisteredDomain} class which is no longer maintained.
* It should be updated or replaced with an appropriate implementation.
*
* @param domain the domain name
* @return an {@code Optional<RegisteredDomain>}; the {@code Optional} is
* empty if the domain is unknown or not registerable
* @throws NullPointerException if domain is null
*/
public static Optional<RegisteredDomain> from(String domain) {
if (domain == null) {
throw new NullPointerException();
}
return Optional.ofNullable(sun.net.RegisteredDomain.registeredDomain(domain));
}
}