8218873: Improve JSSE endpoint checking

Reviewed-by: mullan, ahgross, rhalade
This commit is contained in:
Xue-Lei Andrew Fan 2019-04-11 17:58:18 -07:00
parent efecbce7fe
commit bac946663d

View file

@ -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 @@ public class HostnameChecker {
* 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 @@ public class HostnameChecker {
/**
* 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 @@ public class HostnameChecker {
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;