[httpcomponents-client/f20] Fix MITM security vulnerability
Michal Srb
msrb at fedoraproject.org
Mon Aug 18 13:44:50 UTC 2014
commit 203510431e7214f07939ad2f4743db84f65e0815
Author: Michal Srb <msrb at redhat.com>
Date: Mon Aug 18 15:19:04 2014 +0200
Fix MITM security vulnerability
- Resolves: CVE-2014-3577
0001-Fix-CVE-2014-3577.patch | 185 ++++++++++++++++++++++++++++++++++++++++++
httpcomponents-client.spec | 9 ++-
2 files changed, 193 insertions(+), 1 deletions(-)
---
diff --git a/0001-Fix-CVE-2014-3577.patch b/0001-Fix-CVE-2014-3577.patch
new file mode 100644
index 0000000..9912cfe
--- /dev/null
+++ b/0001-Fix-CVE-2014-3577.patch
@@ -0,0 +1,185 @@
+diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java b/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java
+index a7cad68..7245781 100644
+--- a/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java
++++ b/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java
+@@ -28,7 +28,6 @@
+ package org.apache.http.conn.ssl;
+
+ import org.apache.http.annotation.Immutable;
+-
+ import org.apache.http.conn.util.InetAddressUtils;
+
+ import java.io.IOException;
+@@ -36,14 +35,21 @@ import java.io.InputStream;
+ import java.security.cert.Certificate;
+ import java.security.cert.CertificateParsingException;
+ import java.security.cert.X509Certificate;
++import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Collection;
+ import java.util.Iterator;
+ import java.util.LinkedList;
+ import java.util.List;
+ import java.util.Locale;
+-import java.util.StringTokenizer;
+-
++import java.util.NoSuchElementException;
++
++import javax.naming.InvalidNameException;
++import javax.naming.NamingException;
++import javax.naming.directory.Attribute;
++import javax.naming.directory.Attributes;
++import javax.naming.ldap.LdapName;
++import javax.naming.ldap.Rdn;
+ import javax.net.ssl.SSLException;
+ import javax.net.ssl.SSLSession;
+ import javax.net.ssl.SSLSocket;
+@@ -142,7 +148,8 @@ public abstract class AbstractVerifier implements X509HostnameVerifier {
+
+ public final void verify(String host, X509Certificate cert)
+ throws SSLException {
+- String[] cns = getCNs(cert);
++ final String subjectPrincipal = cert.getSubjectX500Principal().toString();
++ final String[] cns = extractCNs(subjectPrincipal);
+ String[] subjectAlts = getSubjectAlts(cert, host);
+ verify(host, cns, subjectAlts);
+ }
+@@ -236,48 +243,42 @@ public abstract class AbstractVerifier implements X509HostnameVerifier {
+ return Arrays.binarySearch(BAD_COUNTRY_2LDS, parts[1]) < 0;
+ }
+
+- public static String[] getCNs(X509Certificate cert) {
+- LinkedList<String> cnList = new LinkedList<String>();
+- /*
+- Sebastian Hauer's original StrictSSLProtocolSocketFactory used
+- getName() and had the following comment:
+-
+- Parses a X.500 distinguished name for the value of the
+- "Common Name" field. This is done a bit sloppy right
+- now and should probably be done a bit more according to
+- <code>RFC 2253</code>.
+-
+- I've noticed that toString() seems to do a better job than
+- getName() on these X500Principal objects, so I'm hoping that
+- addresses Sebastian's concern.
+-
+- For example, getName() gives me this:
+- 1.2.840.113549.1.9.1=#16166a756c6975736461766965734063756362632e636f6d
+-
+- whereas toString() gives me this:
+- EMAILADDRESS=juliusdavies at cucbc.com
+-
+- Looks like toString() even works with non-ascii domain names!
+- I tested it with "花子.co.jp" and it worked fine.
+- */
+-
+- String subjectPrincipal = cert.getSubjectX500Principal().toString();
+- StringTokenizer st = new StringTokenizer(subjectPrincipal, ",");
+- while(st.hasMoreTokens()) {
+- String tok = st.nextToken().trim();
+- if (tok.length() > 3) {
+- if (tok.substring(0, 3).equalsIgnoreCase("CN=")) {
+- cnList.add(tok.substring(3));
+- }
+- }
++ public static String[] getCNs(final X509Certificate cert) {
++ final String subjectPrincipal = cert.getSubjectX500Principal().toString();
++ try {
++ return extractCNs(subjectPrincipal);
++ } catch (SSLException ex) {
++ return null;
+ }
+- if(!cnList.isEmpty()) {
+- String[] cns = new String[cnList.size()];
+- cnList.toArray(cns);
+- return cns;
+- } else {
++ }
++
++ static String[] extractCNs(final String subjectPrincipal) throws SSLException {
++ if (subjectPrincipal == null) {
+ return null;
+ }
++ final List<String> cns = new ArrayList<String>();
++ try {
++ final LdapName subjectDN = new LdapName(subjectPrincipal);
++ final List<Rdn> rdns = subjectDN.getRdns();
++ for (int i = rdns.size() - 1; i >= 0; i--) {
++ final Rdn rds = rdns.get(i);
++ final Attributes attributes = rds.toAttributes();
++ final Attribute cn = attributes.get("cn");
++ if (cn != null) {
++ try {
++ final Object value = cn.get();
++ if (value != null) {
++ cns.add(value.toString());
++ }
++ } catch (NoSuchElementException ignore) {
++ } catch (NamingException ignore) {
++ }
++ }
++ }
++ } catch (InvalidNameException e) {
++ throw new SSLException(subjectPrincipal + " is not a valid X500 distinguished name");
++ }
++ return cns.isEmpty() ? null : cns.toArray(new String[cns.size()]);
+ }
+
+ /**
+diff --git a/httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java b/httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java
+index 200a6b5..b3ef4cd 100644
+--- a/httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java
++++ b/httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java
+@@ -29,7 +29,6 @@ package org.apache.http.conn.ssl;
+
+ import java.io.ByteArrayInputStream;
+ import java.io.InputStream;
+-import java.security.Principal;
+ import java.security.cert.CertificateFactory;
+ import java.security.cert.X509Certificate;
+ import java.util.Arrays;
+@@ -38,7 +37,6 @@ import javax.net.ssl.SSLException;
+
+ import org.junit.Assert;
+ import org.junit.Test;
+-import org.mockito.Mockito;
+
+ /**
+ * Unit tests for {@link X509HostnameVerifier}.
+@@ -348,16 +346,26 @@ public class TestHostnameVerifier {
+ checkWildcard("s*.a.co.uk", true); // 2 character TLD, invalid 2TLD, but using subdomain
+ }
+
+- public void testGetCNs() {
+- Principal principal = Mockito.mock(Principal.class);
+- X509Certificate cert = Mockito.mock(X509Certificate.class);
+- Mockito.when(cert.getSubjectDN()).thenReturn(principal);
+- Mockito.when(principal.toString()).thenReturn("bla, bla, blah");
+- Assert.assertArrayEquals(new String[] {}, AbstractVerifier.getCNs(cert));
+- Mockito.when(principal.toString()).thenReturn("Cn=, Cn= , CN, OU=CN=");
+- Assert.assertArrayEquals(new String[] {}, AbstractVerifier.getCNs(cert));
+- Mockito.when(principal.toString()).thenReturn(" Cn=blah, CN= blah , OU=CN=yada");
+- Assert.assertArrayEquals(new String[] {"blah", " blah"}, AbstractVerifier.getCNs(cert));
++ @Test
++ public void testExtractCN() throws Exception {
++ Assert.assertArrayEquals(new String[] { "blah" }, AbstractVerifier.extractCNs("cn=blah, ou=blah, o=blah"));
++ Assert.assertArrayEquals(new String[] { "blah", "yada", "booh" }, AbstractVerifier.extractCNs("cn=blah, cn=yada, cn=booh"));
++ Assert.assertArrayEquals(new String[] { "blah" }, AbstractVerifier.extractCNs("c = pampa , cn = blah , ou = blah , o = blah"));
++ Assert.assertArrayEquals(new String[] { "blah" }, AbstractVerifier.extractCNs("cn=\"blah\", ou=blah, o=blah"));
++ Assert.assertArrayEquals(new String[] { "blah blah" }, AbstractVerifier.extractCNs("cn=\"blah blah\", ou=blah, o=blah"));
++ Assert.assertArrayEquals(new String[] { "blah, blah" }, AbstractVerifier.extractCNs("cn=\"blah, blah\", ou=blah, o=blah"));
++ Assert.assertArrayEquals(new String[] { "blah, blah" }, AbstractVerifier.extractCNs("cn=blah\\, blah, ou=blah, o=blah"));
++ Assert.assertArrayEquals(new String[] { "blah" }, AbstractVerifier.extractCNs("c = cn=uuh, cn=blah, ou=blah, o=blah"));
++ Assert.assertArrayEquals(new String[] { "" }, AbstractVerifier.extractCNs("cn= , ou=blah, o=blah"));
++ }
++
++ @Test(expected = SSLException.class)
++ public void testExtractCNInvalid1() throws Exception {
++ AbstractVerifier.extractCNs("blah,blah");
+ }
+
++ @Test(expected = SSLException.class)
++ public void testExtractCNInvalid2() throws Exception {
++ AbstractVerifier.extractCNs("cn,o=blah");
++ }
+ }
diff --git a/httpcomponents-client.spec b/httpcomponents-client.spec
index fbbd3ea..8fd53f3 100644
--- a/httpcomponents-client.spec
+++ b/httpcomponents-client.spec
@@ -3,11 +3,12 @@
Name: httpcomponents-client
Summary: HTTP agent implementation based on httpcomponents HttpCore
Version: 4.2.5
-Release: 3%{?dist}
+Release: 4%{?dist}
Group: Development/Libraries
License: ASL 2.0
URL: http://hc.apache.org/
Source0: http://archive.apache.org/dist/httpcomponents/httpclient/source/%{name}-%{version}-src.tar.gz
+Patch0: 0001-Fix-CVE-2014-3577.patch
BuildArch: noarch
@@ -41,6 +42,8 @@ Group: Documentation
%prep
%setup -q
+%patch0 -p1
+
# Remove optional build deps not available in Fedora
%pom_disable_module httpclient-cache
%pom_disable_module httpclient-osgi
@@ -122,6 +125,10 @@ done
%doc LICENSE.txt NOTICE.txt
%changelog
+* Mon Aug 18 2014 Michal Srb <msrb at redhat.com> - 4.2.5-4
+- Fix MITM security vulnerability
+- Resolves: CVE-2014-3577
+
* Sat Aug 03 2013 Fedora Release Engineering <rel-eng at lists.fedoraproject.org> - 4.2.5-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
More information about the scm-commits
mailing list