Reverted to simple wildcard matching

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1622696 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2014-09-05 13:17:30 +00:00
parent 2466424d5d
commit 9e83772d35
5 changed files with 64 additions and 51 deletions

View File

@ -177,20 +177,46 @@ public abstract class AbstractVerifier implements X509HostnameVerifier {
}
}
private static boolean matchIdentity(final String host, final String identity, final boolean strictWithSubDomains) {
return strictWithSubDomains ?
DefaultHostnameVerifier.matchIdentityStrict(host, identity) :
DefaultHostnameVerifier.matchIdentity(host, identity);
private static boolean matchIdentity(final String host, final String identity, final boolean strict) {
if (host == null) {
return false;
}
final String normalizedHost = host.toLowerCase(Locale.ROOT);
final String normalizedIdentity = identity.toLowerCase(Locale.ROOT);
// The CN better have at least two dots if it wants wildcard
// action. It also can't be [*.co.uk] or [*.co.jp] or
// [*.org.uk], etc...
final String parts[] = normalizedIdentity.split("\\.");
final boolean doWildcard = parts.length >= 3 && parts[0].endsWith("*") &&
(!strict || validCountryWildcard(parts));
if (doWildcard) {
boolean match;
final String firstpart = parts[0];
if (firstpart.length() > 1) { // e.g. server*
final String prefix = firstpart.substring(0, firstpart.length() - 1); // e.g. server
final String suffix = normalizedIdentity.substring(firstpart.length()); // skip wildcard part from cn
final String hostSuffix = normalizedHost.substring(prefix.length()); // skip wildcard part from normalizedHost
match = normalizedHost.startsWith(prefix) && hostSuffix.endsWith(suffix);
} else {
match = normalizedHost.endsWith(normalizedIdentity.substring(1));
}
return match && (!strict || countDots(normalizedHost) == countDots(normalizedIdentity));
} else {
return normalizedHost.equals(normalizedIdentity);
}
}
public static boolean acceptableCountryWildcard(final String cn) {
final String parts[] = cn.split("\\.");
private static boolean validCountryWildcard(final String parts[]) {
if (parts.length != 3 || parts[2].length() != 2) {
return true; // it's not an attempt to wildcard a 2TLD within a country code
}
return Arrays.binarySearch(BAD_COUNTRY_2LDS, parts[1]) < 0;
}
public static boolean acceptableCountryWildcard(final String cn) {
return validCountryWildcard(cn.split("\\."));
}
public static String[] getCNs(final X509Certificate cert) {
final String subjectPrincipal = cert.getSubjectX500Principal().toString();
try {

View File

@ -35,9 +35,9 @@ import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.naming.InvalidNameException;
import javax.naming.NamingException;
@ -63,11 +63,6 @@ import org.apache.http.conn.util.InetAddressUtils;
@Immutable
public final class DefaultHostnameVerifier implements HostnameVerifier {
public static final DefaultHostnameVerifier INSTANCE = new DefaultHostnameVerifier();
private final static Pattern WILDCARD_PATTERN = Pattern.compile(
"^[a-z0-9\\-\\*]+(\\.[a-z0-9\\-]+){2,}$",
Pattern.CASE_INSENSITIVE);
/**
* This contains a list of 2nd-level domains that aren't allowed to
* have wildcards when combined with country-codes.
@ -155,9 +150,11 @@ public final class DefaultHostnameVerifier implements HostnameVerifier {
}
static void matchDNSName(final String host, final List<String> subjectAlts) throws SSLException {
final String normalizedHost = host.toLowerCase(Locale.ROOT);
for (int i = 0; i < subjectAlts.size(); i++) {
final String subjectAlt = subjectAlts.get(i);
if (matchIdentityStrict(host, subjectAlt)) {
final String normalizedSubjectAlt = subjectAlt.toLowerCase(Locale.ROOT);
if (matchIdentityStrict(normalizedHost, normalizedSubjectAlt)) {
return;
}
}
@ -176,34 +173,31 @@ public final class DefaultHostnameVerifier implements HostnameVerifier {
if (host == null) {
return false;
}
// The CN better have at least two dots if it wants wildcard
// action. It also can't be [*.co.uk] or [*.co.jp] or
// [*.org.uk], etc...
if (identity.contains("*") && WILDCARD_PATTERN.matcher(identity).matches()) {
// RFC 2818, 3.1. Server Identity
// "...Names may contain the wildcard
// character * which is considered to match any single domain name
// component or component fragment..."
// Based on this statement presuming only singular wildcard is legal
final int asteriskIdx = identity.indexOf('*');
if (asteriskIdx != -1) {
if (!strict || !BAD_COUNTRY_WILDCARD_PATTERN.matcher(identity).matches()) {
final StringBuilder buf = new StringBuilder();
buf.append("^");
for (int i = 0; i < identity.length(); i++) {
final char ch = identity.charAt(i);
if (ch == '.') {
buf.append("\\.");
} else if (ch == '*') {
if (strict) {
buf.append("[a-z0-9\\-]*");
} else {
buf.append(".*");
}
} else {
buf.append(ch);
final String prefix = identity.substring(0, asteriskIdx);
final String suffix = identity.substring(asteriskIdx + 1);
if (!prefix.isEmpty() && !host.startsWith(prefix)) {
return false;
}
if (!suffix.isEmpty() && !host.endsWith(suffix)) {
return false;
}
// Additional sanity checks on content selected by wildcard can be done here
if (strict) {
final String remainder = host.substring(
prefix.length(), host.length() - suffix.length());
if (remainder.contains(".")) {
return false;
}
}
buf.append("$");
try {
final Pattern identityPattern = Pattern.compile(buf.toString(), Pattern.CASE_INSENSITIVE);
return identityPattern.matcher(host).matches();
} catch (PatternSyntaxException ignore) {
// do simple match
}
return true;
}
}
return host.equalsIgnoreCase(identity);

View File

@ -148,7 +148,7 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
* @since 4.4
*/
public static HostnameVerifier getDefaultHostnameVerifier() {
return DefaultHostnameVerifier.INSTANCE;
return new DefaultHostnameVerifier();
}
/**

View File

@ -48,7 +48,7 @@ public class TestDefaultHostnameVerifier {
@Before
public void setup() {
impl = DefaultHostnameVerifier.INSTANCE;
impl = new DefaultHostnameVerifier();
}
@Test
@ -113,7 +113,7 @@ public class TestDefaultHostnameVerifier {
x509 = (X509Certificate) cf.generateCertificate(in);
exceptionPlease(impl, "foo.com", x509);
impl.verify("www.foo.com", x509);
exceptionPlease(impl, "\u82b1\u5b50.foo.com", x509);
impl.verify("\u82b1\u5b50.foo.com", x509);
exceptionPlease(impl, "a.b.foo.com", x509);
in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_CO_JP);
@ -134,7 +134,7 @@ public class TestDefaultHostnameVerifier {
// try the bar.com variations
exceptionPlease(impl, "bar.com", x509);
impl.verify("www.bar.com", x509);
exceptionPlease(impl, "\u82b1\u5b50.bar.com", x509);
impl.verify("\u82b1\u5b50.bar.com", x509);
exceptionPlease(impl, "a.b.bar.com", x509);
in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_VALUE_AVA);
@ -191,13 +191,6 @@ public class TestDefaultHostnameVerifier {
Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.c"));
Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.c"));
Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.B.c", "*.b.c"));
Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.B.c", "*.b.c"));
Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.C", "*.B.c"));
Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.C", "*.B.c"));
Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.b.c", "*.b.c"));
Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.b.c", "*.b.c")); // subdomain not OK

View File

@ -139,7 +139,7 @@ public class TestHostnameVerifier {
DEFAULT.verify("www.foo.com", x509);
STRICT.verify("www.foo.com", x509);
DEFAULT.verify("\u82b1\u5b50.foo.com", x509);
exceptionPlease(STRICT, "\u82b1\u5b50.foo.com", x509);
STRICT.verify("\u82b1\u5b50.foo.com", x509);
DEFAULT.verify("a.b.foo.com", x509);
exceptionPlease(STRICT, "a.b.foo.com", x509);
@ -171,7 +171,7 @@ public class TestHostnameVerifier {
DEFAULT.verify("www.bar.com", x509);
STRICT.verify("www.bar.com", x509);
DEFAULT.verify("\u82b1\u5b50.bar.com", x509);
exceptionPlease(STRICT, "\u82b1\u5b50.bar.com", x509);
STRICT.verify("\u82b1\u5b50.bar.com", x509);
DEFAULT.verify("a.b.bar.com", x509);
exceptionPlease(STRICT, "a.b.bar.com", x509);