diff options
Diffstat (limited to 'luni')
6 files changed, 76 insertions, 230 deletions
diff --git a/luni/src/main/files/README.cacerts b/luni/src/main/files/README.cacerts index e905a68..ca5c570 100755 --- a/luni/src/main/files/README.cacerts +++ b/luni/src/main/files/README.cacerts @@ -1,7 +1,7 @@ The filenames in the cacerts directory are in the format of <hash>.<n> where "hash" is the subject hash produced by: - openssl x509 -subject_hash -in filename + openssl x509 -subject_hash_old -in filename and the "n" is a unique integer identifier starting at 0 to deal with collisions. See OpenSSL's c_rehash manpage for details. diff --git a/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java b/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java deleted file mode 100644 index e513cf2..0000000 --- a/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package javax.net.ssl; - -import java.net.InetAddress; -import java.security.cert.Certificate; -import java.security.cert.CertificateParsingException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import javax.security.auth.x500.X500Principal; - -/** - * A HostnameVerifier consistent with <a - * href="http://www.ietf.org/rfc/rfc2818.txt">RFC 2818</a>. - * - * @hide accessible via HttpsURLConnection.getDefaultHostnameVerifier() - */ -public final class DefaultHostnameVerifier implements HostnameVerifier { - private static final int ALT_DNS_NAME = 2; - private static final int ALT_IPA_NAME = 7; - - @Override - public final boolean verify(String host, SSLSession session) { - try { - Certificate[] certificates = session.getPeerCertificates(); - return verify(host, (X509Certificate) certificates[0]); - } catch (SSLException e) { - return false; - } - } - - private boolean verify(String host, X509Certificate certificate) { - return InetAddress.isNumeric(host) - ? verifyIpAddress(host, certificate) - : verifyHostName(host, certificate); - } - - /** - * Returns true if {@code certificate} matches {@code ipAddress}. - */ - private boolean verifyIpAddress(String ipAddress, X509Certificate certificate) { - for (String altName : getSubjectAltNames(certificate, ALT_IPA_NAME)) { - if (ipAddress.equalsIgnoreCase(altName)) { - return true; - } - } - return false; - } - - /** - * Returns true if {@code certificate} matches {@code hostName}. - */ - private boolean verifyHostName(String hostName, X509Certificate certificate) { - hostName = hostName.toLowerCase(Locale.US); - boolean hasDns = false; - for (String altName : getSubjectAltNames(certificate, ALT_DNS_NAME)) { - hasDns = true; - if (verifyHostName(hostName, altName)) { - return true; - } - } - - if (!hasDns) { - X500Principal principal = certificate.getSubjectX500Principal(); - // RFC 2818 advises using the most specific name for matching. - String cn = new DistinguishedNameParser(principal).findMostSpecific("cn"); - if (cn != null) { - return verifyHostName(hostName, cn); - } - } - - return false; - } - - private List<String> getSubjectAltNames(X509Certificate certificate, int type) { - List<String> result = new ArrayList<String>(); - try { - Collection<?> subjectAltNames = certificate.getSubjectAlternativeNames(); - if (subjectAltNames == null) { - return Collections.emptyList(); - } - for (Object subjectAltName : subjectAltNames) { - List<?> entry = (List<?>) subjectAltName; - if (entry == null || entry.size() < 2) { - continue; - } - Integer altNameType = (Integer) entry.get(0); - if (altNameType == null) { - continue; - } - if (altNameType == type) { - String altName = (String) entry.get(1); - if (altName != null) { - result.add(altName); - } - } - } - return result; - } catch (CertificateParsingException e) { - return Collections.emptyList(); - } - } - - /** - * Returns true if {@code hostName} matches the name or pattern {@code cn}. - * - * @param hostName lowercase host name. - * @param cn certificate host name. May include wildcards like - * {@code *.android.com}. - */ - private boolean verifyHostName(String hostName, String cn) { - if (hostName == null || hostName.isEmpty() || cn == null || cn.isEmpty()) { - return false; - } - - if (hostName.endsWith(".") && !cn.endsWith(".")) { - // "www.android.com." matches "www.android.com" - // This is needed because server certificates do not normally contain absolute names - // or patterns. Connections via absolute hostnames should be supported and even - // preferred over those via relative hostnames, to avoid DNS suffixes being appended. - cn += '.'; - } - - cn = cn.toLowerCase(Locale.US); - - if (!cn.contains("*")) { - return hostName.equals(cn); - } - - if (!containsAtLeastTwoDomainNameLabelsExcludingRoot(cn)) { - return false; // reject matches where the wildcard pattern consists of only one label. - } - - if (cn.startsWith("*.") && hostName.regionMatches(0, cn, 2, cn.length() - 2)) { - return true; // "*.foo.com" matches "foo.com" - } - - int asterisk = cn.indexOf('*'); - int dot = cn.indexOf('.'); - if (asterisk > dot) { - return false; // malformed; wildcard must be in the first part of the cn - } - - if (!hostName.regionMatches(0, cn, 0, asterisk)) { - return false; // prefix before '*' doesn't match - } - - int suffixLength = cn.length() - (asterisk + 1); - int suffixStart = hostName.length() - suffixLength; - if (hostName.indexOf('.', asterisk) < suffixStart) { - return false; // wildcard '*' can't match a '.' - } - - if (!hostName.regionMatches(suffixStart, cn, asterisk + 1, suffixLength)) { - return false; // suffix after '*' doesn't match - } - - return true; - } - - /** - * Checks whether the provided hostname consists of at least two domain name labels, excluding - * the root label. - * - * <p>For example, this method returns {@code true} for {@code www.android.com} and - * {@code foo.com} and {@code foo.com.}, and returns {@code false} for {@code foo} and - * {@code foo.}. - */ - private static boolean containsAtLeastTwoDomainNameLabelsExcludingRoot(String hostname) { - int delimiterIndex = hostname.indexOf('.'); - if (delimiterIndex == -1) { - // No delimiters -- only one label - return false; - } - if (delimiterIndex == hostname.length() - 1) { - // Only one delimiter at the every end of the hostname -- this is an absolute hostname - // consisting of one label - return false; - } - // At least two labels - return true; - } -} diff --git a/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java b/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java index ab86a9b..1bd48fd 100644 --- a/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java +++ b/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java @@ -109,7 +109,16 @@ public abstract class HttpsURLConnection extends HttpURLConnection { * it. */ private static class NoPreloadHolder { - public static HostnameVerifier defaultHostnameVerifier = new DefaultHostnameVerifier(); + public static HostnameVerifier defaultHostnameVerifier; + static { + try { + defaultHostnameVerifier = (HostnameVerifier) + Class.forName("com.android.okhttp.internal.tls.OkHostnameVerifier") + .getField("INSTANCE").get(null); + } catch (Exception e) { + throw new AssertionError("Failed to obtain okhttp HostnameVerifier", e); + } + } public static SSLSocketFactory defaultSSLSocketFactory = (SSLSocketFactory) SSLSocketFactory .getDefault(); diff --git a/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java b/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java index ecf2e5f..7a5fc4a 100644 --- a/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java +++ b/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java @@ -108,7 +108,7 @@ public class OldTimeZoneTest extends TestCase { TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); assertEquals("Pacific Daylight Time", tz.getDisplayName(true, TimeZone.LONG, Locale.US)); assertEquals("Pacific Standard Time", tz.getDisplayName(false, TimeZone.LONG, Locale.UK)); - assertEquals("heure avanc\u00e9e du Pacifique", + assertEquals("heure d’été du Pacifique", tz.getDisplayName(true, TimeZone.LONG, Locale.FRANCE)); assertEquals("heure normale du Pacifique nord-américain", tz.getDisplayName(false, TimeZone.LONG, Locale.FRANCE)); @@ -123,18 +123,6 @@ public class OldTimeZoneTest extends TestCase { assertEquals("GMT-07:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.FRANCE)); assertEquals("GMT-08:00", tz.getDisplayName(false, TimeZone.SHORT, Locale.UK)); assertEquals("GMT-07:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.UK)); - - // The RI behavior mentioned above does not appear to be because "PST" is a legacy - // three-character timezone supported by the RI: it happens for "Asia/Tehran"/"IRST" too - // (IRST is not a legacy code). The RI may just use a different dataset that has "PST" / - // "IRST" as valid translations (even for scripts like Chinese). - TimeZone iranTz = TimeZone.getTimeZone("Asia/Tehran"); - assertEquals("Iran Summer Time", iranTz.getDisplayName(true, TimeZone.LONG, Locale.UK)); - assertEquals("Iran Daylight Time", iranTz.getDisplayName(true, TimeZone.LONG, Locale.US)); - assertEquals("Iran Standard Time", iranTz.getDisplayName(false, TimeZone.LONG, Locale.UK)); - assertEquals("Iran Standard Time", iranTz.getDisplayName(false, TimeZone.LONG, Locale.US)); - assertEquals("GMT+03:30", iranTz.getDisplayName(false, TimeZone.SHORT, Locale.UK)); - assertEquals("GMT+04:30", iranTz.getDisplayName(true, TimeZone.SHORT, Locale.UK)); } public void test_getID() { diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java index aadb9df..96ad2ed 100644 --- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java +++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java @@ -73,6 +73,12 @@ public class TimeZoneTest extends TestCase { assertFalse(tz.inDaylightTime(date)); } + public void testGetDisplayNameShort_nonHourOffsets() { + TimeZone iranTz = TimeZone.getTimeZone("Asia/Tehran"); + assertEquals("GMT+03:30", iranTz.getDisplayName(false, TimeZone.SHORT, Locale.UK)); + assertEquals("GMT+04:30", iranTz.getDisplayName(true, TimeZone.SHORT, Locale.UK)); + } + public void testPreHistoricOffsets() throws Exception { // "Africa/Bissau" has just a few transitions and hasn't changed in a long time. // 1912-01-01 00:02:19-0100 ... 1912-01-01 00:02:20-0100 diff --git a/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java index 9e4b804..1a24667 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java @@ -124,25 +124,70 @@ public final class DefaultHostnameVerifierTest extends TestCase { .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3"))); } - public void testWildcardMatchesWildcardSuffix() { - assertTrue(verifyWithDomainNamePattern("b.c.d", "*.b.c.d")); - assertTrue(verifyWithDomainNamePattern("imap.google.com", "*.imap.google.com")); + public void testWildcardsRejectedForIpAddress() { + assertFalse(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("cn=*.2.3.4"))); + assertFalse(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("cn=*.2.3.4") + .addSubjectAlternativeName(ALT_IPA_NAME, "*.2.3.4") + .addSubjectAlternativeName(ALT_DNS_NAME, "*.2.3.4"))); + assertFalse(verifyWithServerCertificate( + "2001:1234::1", new StubX509Certificate("cn=*:1234::1"))); + assertFalse(verifyWithServerCertificate( + "2001:1234::1", new StubX509Certificate("cn=*:1234::1") + .addSubjectAlternativeName(ALT_IPA_NAME, "*:1234::1") + .addSubjectAlternativeName(ALT_DNS_NAME, "*:1234::1"))); + } + + public void testNullParameters() { + // Confirm that neither of the parameters used later in the test cause the verifier to blow + // up + String hostname = "www.example.com"; + StubSSLSession session = new StubSSLSession(); + session.peerCertificates = + new Certificate[] {new StubX509Certificate("cn=www.example.com")}; + verifier.verify(hostname, session); + + try { + verifier.verify(hostname, null); + fail(); + } catch (NullPointerException expected) { + } + + try { + verifier.verify(null, session); + fail(); + } catch (NullPointerException expected) { + } + } + + public void testInvalidDomainNames() { + assertFalse(verifyWithDomainNamePattern("", "")); + assertFalse(verifyWithDomainNamePattern(".test.example.com", ".test.example.com")); + assertFalse(verifyWithDomainNamePattern("ex*ample.com", "ex*ample.com")); + assertFalse(verifyWithDomainNamePattern("example.com..", "example.com.")); + assertFalse(verifyWithDomainNamePattern("example.com.", "example.com..")); } - public void testWildcardMatchingSubstring() { - assertTrue(verifyWithDomainNamePattern("b.c.d", "b*.c.d")); - assertTrue(verifyWithDomainNamePattern("imap.google.com", "ima*.google.com")); + public void testWildcardCharacterMustBeLeftMostLabelOnly() { + assertFalse(verifyWithDomainNamePattern("test.www.example.com", "test.*.example.com")); + assertFalse(verifyWithDomainNamePattern("www.example.com", "www.*.com")); + assertFalse(verifyWithDomainNamePattern("www.example.com", "www.example.*")); + assertFalse(verifyWithDomainNamePattern("www.example.com", "*www.example.com")); + assertFalse(verifyWithDomainNamePattern("www.example.com", "*w.example.com")); + assertFalse(verifyWithDomainNamePattern("www.example.com", "w*w.example.com")); + assertFalse(verifyWithDomainNamePattern("www.example.com", "w*.example.com")); + assertFalse(verifyWithDomainNamePattern("www.example.com", "www*.example.com")); } - public void testWildcardMatchingEmptySubstring() { - assertTrue(verifyWithDomainNamePattern("imap.google.com", "imap*.google.com")); + public void testWildcardCannotMatchEmptyLabel() { + assertFalse(verifyWithDomainNamePattern("example.com", "*.example.com")); + assertFalse(verifyWithDomainNamePattern(".example.com", "*.example.com")); } - public void testWildcardMatchesChildDomain() { - assertFalse(verifyWithDomainNamePattern("a.b.c.d", "*.c.d")); + public void testWildcardCannotMatchChildDomain() { + assertFalse(verifyWithDomainNamePattern("sub.www.example.com", "*.example.com")); } - public void testWildcardsRejectedForSingleLabelPatterns() { + public void testWildcardRejectedForSingleLabelPatterns() { assertFalse(verifyWithDomainNamePattern("d", "*")); assertFalse(verifyWithDomainNamePattern("d.", "*.")); assertFalse(verifyWithDomainNamePattern("d", "d*")); @@ -167,7 +212,7 @@ public final class DefaultHostnameVerifierTest extends TestCase { assertFalse(verifyWithDomainNamePattern("imap.google.com", "ix*.google.com")); assertTrue(verifyWithDomainNamePattern("imap.google.com", "iMap.Google.Com")); assertTrue(verifyWithDomainNamePattern("weird", "weird")); - assertFalse(verifyWithDomainNamePattern("weird", "weird.")); + assertTrue(verifyWithDomainNamePattern("weird", "weird.")); // Wildcards rejected for domain names consisting of fewer than two labels (excluding root). assertFalse(verifyWithDomainNamePattern("weird", "weird*")); @@ -364,7 +409,7 @@ public final class DefaultHostnameVerifierTest extends TestCase { // Verify using a certificate where the pattern is in the CN session.peerCertificates = new Certificate[] { - new StubX509Certificate("cn=" + pattern) + new StubX509Certificate("cn=\"" + pattern + "\"") }; boolean resultWhenPatternInCn = verifier.verify(hostname, session); |