[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 "&#x82b1;&#x5b50;.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