From 3e3515b42a5c782219ba898f9cb79812c8895349 Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Tue, 12 Aug 2014 14:07:29 +0200 Subject: [PATCH] Fix CVE-2014-3577 --- .../org/apache/http/conn/ssl/AbstractVerifier.java | 87 +++++++++++----------- 1 file changed, 44 insertions(+), 43 deletions(-) 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 cnList = new LinkedList(); - /* - 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 - RFC 2253. - - 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@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 cns = new ArrayList(); + try { + final LdapName subjectDN = new LdapName(subjectPrincipal); + final List 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()]); } /** -- 1.9.3