You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2018/02/08 18:55:55 UTC
httpcomponents-client git commit: HTTPCLIENT-1903:
DefaultHostnameVerifier to use a custom distinguished name (DN) parser
instead of LdapName; removed dependency on Java Naming extensions
Repository: httpcomponents-client
Updated Branches:
refs/heads/4.6.x 4eeeb2789 -> 30b8f8245
HTTPCLIENT-1903: DefaultHostnameVerifier to use a custom distinguished name (DN) parser instead of LdapName; removed dependency on Java Naming extensions
Project: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/commit/30b8f824
Tree: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/tree/30b8f824
Diff: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/diff/30b8f824
Branch: refs/heads/4.6.x
Commit: 30b8f824588af3f43d4266a17453f36f6f2f13c4
Parents: 4eeeb27
Author: Oleg Kalnichevski <ol...@apache.org>
Authored: Fri Jan 12 18:47:56 2018 +0100
Committer: Oleg Kalnichevski <ol...@apache.org>
Committed: Thu Feb 8 19:51:48 2018 +0100
----------------------------------------------------------------------
.../http/conn/ssl/DefaultHostnameVerifier.java | 39 ++---
.../http/conn/ssl/DistinguishedNameParser.java | 134 ++++++++++++++++++
.../conn/ssl/TestDistinguishedNameParser.java | 141 +++++++++++++++++++
3 files changed, 285 insertions(+), 29 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/30b8f824/httpclient/src/main/java/org/apache/http/conn/ssl/DefaultHostnameVerifier.java
----------------------------------------------------------------------
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/DefaultHostnameVerifier.java b/httpclient/src/main/java/org/apache/http/conn/ssl/DefaultHostnameVerifier.java
index 2202abe..843c636 100644
--- a/httpclient/src/main/java/org/apache/http/conn/ssl/DefaultHostnameVerifier.java
+++ b/httpclient/src/main/java/org/apache/http/conn/ssl/DefaultHostnameVerifier.java
@@ -37,14 +37,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
-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.HostnameVerifier;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
@@ -53,11 +46,13 @@ import javax.security.auth.x500.X500Principal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.http.NameValuePair;
import org.apache.http.annotation.Contract;
import org.apache.http.annotation.ThreadingBehavior;
import org.apache.http.conn.util.DomainType;
import org.apache.http.conn.util.InetAddressUtils;
import org.apache.http.conn.util.PublicSuffixMatcher;
+import org.apache.http.util.TextUtils;
/**
* Default {@link javax.net.ssl.HostnameVerifier} implementation.
@@ -255,30 +250,16 @@ public final class DefaultHostnameVerifier implements HostnameVerifier {
if (subjectPrincipal == null) {
return null;
}
- 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) {
- return value.toString();
- }
- } catch (final NoSuchElementException ignore) {
- // ignore exception
- } catch (final NamingException ignore) {
- // ignore exception
- }
- }
+ final List<NameValuePair> attributes = DistinguishedNameParser.INSTANCE.parse(subjectPrincipal);
+ for (final NameValuePair attribute: attributes) {
+ if (TextUtils.isBlank(attribute.getName()) || attribute.getValue() == null) {
+ throw new SSLException(subjectPrincipal + " is not a valid X500 distinguished name");
+ }
+ if (attribute.getName().equalsIgnoreCase("cn")) {
+ return attribute.getValue();
}
- return null;
- } catch (final InvalidNameException e) {
- throw new SSLException(subjectPrincipal + " is not a valid X500 distinguished name");
}
+ return null;
}
static HostNameType determineHostFormat(final String host) {
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/30b8f824/httpclient/src/main/java/org/apache/http/conn/ssl/DistinguishedNameParser.java
----------------------------------------------------------------------
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/DistinguishedNameParser.java b/httpclient/src/main/java/org/apache/http/conn/ssl/DistinguishedNameParser.java
new file mode 100644
index 0000000..00f5769
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/conn/ssl/DistinguishedNameParser.java
@@ -0,0 +1,134 @@
+/*
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.annotation.Contract;
+import org.apache.http.annotation.ThreadingBehavior;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.message.TokenParser;
+import org.apache.http.util.CharArrayBuffer;
+
+@Contract(threading = ThreadingBehavior.IMMUTABLE)
+final class DistinguishedNameParser {
+
+ public final static DistinguishedNameParser INSTANCE = new DistinguishedNameParser();
+
+ private static final BitSet EQUAL_OR_COMMA_OR_PLUS = TokenParser.INIT_BITSET('=', ',', '+');
+ private static final BitSet COMMA_OR_PLUS = TokenParser.INIT_BITSET(',', '+');
+
+ private final TokenParser tokenParser;
+
+ DistinguishedNameParser() {
+ this.tokenParser = new InternalTokenParser();
+ }
+
+ private String parseToken(final CharArrayBuffer buf, final ParserCursor cursor, final BitSet delimiters) {
+ return tokenParser.parseToken(buf, cursor, delimiters);
+ }
+
+ private String parseValue(final CharArrayBuffer buf, final ParserCursor cursor, final BitSet delimiters) {
+ return tokenParser.parseValue(buf, cursor, delimiters);
+ }
+
+ private NameValuePair parseParameter(final CharArrayBuffer buf, final ParserCursor cursor) {
+ final String name = parseToken(buf, cursor, EQUAL_OR_COMMA_OR_PLUS);
+ if (cursor.atEnd()) {
+ return new BasicNameValuePair(name, null);
+ }
+ final int delim = buf.charAt(cursor.getPos());
+ cursor.updatePos(cursor.getPos() + 1);
+ if (delim == ',') {
+ return new BasicNameValuePair(name, null);
+ }
+ final String value = parseValue(buf, cursor, COMMA_OR_PLUS);
+ if (!cursor.atEnd()) {
+ cursor.updatePos(cursor.getPos() + 1);
+ }
+ return new BasicNameValuePair(name, value);
+ }
+
+ List<NameValuePair> parse(final CharArrayBuffer buf, final ParserCursor cursor) {
+ final List<NameValuePair> params = new ArrayList<NameValuePair>();
+ tokenParser.skipWhiteSpace(buf, cursor);
+ while (!cursor.atEnd()) {
+ final NameValuePair param = parseParameter(buf, cursor);
+ params.add(param);
+ }
+ return params;
+ }
+
+ List<NameValuePair> parse(final String s) {
+ if (s == null) {
+ return null;
+ }
+ final CharArrayBuffer buffer = new CharArrayBuffer(s.length());
+ buffer.append(s);
+ final ParserCursor cursor = new ParserCursor(0, s.length());
+ return parse(buffer, cursor);
+ }
+
+ static class InternalTokenParser extends TokenParser {
+
+ @Override
+ public void copyUnquotedContent(
+ final CharArrayBuffer buf,
+ final ParserCursor cursor,
+ final BitSet delimiters,
+ final StringBuilder dst) {
+ int pos = cursor.getPos();
+ final int indexFrom = cursor.getPos();
+ final int indexTo = cursor.getUpperBound();
+ boolean escaped = false;
+ for (int i = indexFrom; i < indexTo; i++, pos++) {
+ final char current = buf.charAt(i);
+ if (escaped) {
+ dst.append(current);
+ escaped = false;
+ } else {
+ if ((delimiters != null && delimiters.get(current))
+ || TokenParser.isWhitespace(current) || current == '\"') {
+ break;
+ } else if (current == '\\') {
+ escaped = true;
+ } else {
+ dst.append(current);
+ }
+ }
+ }
+ cursor.updatePos(pos);
+ }
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/30b8f824/httpclient/src/test/java/org/apache/http/conn/ssl/TestDistinguishedNameParser.java
----------------------------------------------------------------------
diff --git a/httpclient/src/test/java/org/apache/http/conn/ssl/TestDistinguishedNameParser.java b/httpclient/src/test/java/org/apache/http/conn/ssl/TestDistinguishedNameParser.java
new file mode 100644
index 0000000..c2c1567
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/conn/ssl/TestDistinguishedNameParser.java
@@ -0,0 +1,141 @@
+/*
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+import java.util.Arrays;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.message.BasicNameValuePair;
+import org.hamcrest.CoreMatchers;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link DistinguishedNameParser}.
+ */
+public class TestDistinguishedNameParser {
+
+ private DistinguishedNameParser impl;
+
+ @Before
+ public void setup() {
+ impl = new DistinguishedNameParser();
+ }
+
+ @Test
+ public void testParseBasic() throws Exception {
+ Assert.assertThat(impl.parse("cn=blah, ou=yada, o=booh"),
+ CoreMatchers.equalTo(Arrays.<NameValuePair>asList(
+ new BasicNameValuePair("cn", "blah"),
+ new BasicNameValuePair("ou", "yada"),
+ new BasicNameValuePair("o", "booh"))));
+ }
+
+ @Test
+ public void testParseRepeatedElements() throws Exception {
+ Assert.assertThat(impl.parse("cn=blah, cn=yada, cn=booh"),
+ CoreMatchers.equalTo(Arrays.<NameValuePair>asList(
+ new BasicNameValuePair("cn", "blah"),
+ new BasicNameValuePair("cn", "yada"),
+ new BasicNameValuePair("cn", "booh"))));
+ }
+
+ @Test
+ public void testParseBlanks() throws Exception {
+ Assert.assertThat(impl.parse("c = pampa , cn = blah , ou = blah , o = blah"),
+ CoreMatchers.equalTo(Arrays.<NameValuePair>asList(
+ new BasicNameValuePair("c", "pampa"),
+ new BasicNameValuePair("cn", "blah"),
+ new BasicNameValuePair("ou", "blah"),
+ new BasicNameValuePair("o", "blah"))));
+ }
+
+ @Test
+ public void testParseQuotes() throws Exception {
+ Assert.assertThat(impl.parse("cn=\"blah\", ou=yada, o=booh"),
+ CoreMatchers.equalTo(Arrays.<NameValuePair>asList(
+ new BasicNameValuePair("cn", "blah"),
+ new BasicNameValuePair("ou", "yada"),
+ new BasicNameValuePair("o", "booh"))));
+ }
+
+ @Test
+ public void testParseQuotes2() throws Exception {
+ Assert.assertThat(impl.parse("cn=\"blah blah\", ou=yada, o=booh"),
+ CoreMatchers.equalTo(Arrays.<NameValuePair>asList(
+ new BasicNameValuePair("cn", "blah blah"),
+ new BasicNameValuePair("ou", "yada"),
+ new BasicNameValuePair("o", "booh"))));
+ }
+
+ @Test
+ public void testParseQuotes3() throws Exception {
+ Assert.assertThat(impl.parse("cn=\"blah, blah\", ou=yada, o=booh"),
+ CoreMatchers.equalTo(Arrays.<NameValuePair>asList(
+ new BasicNameValuePair("cn", "blah, blah"),
+ new BasicNameValuePair("ou", "yada"),
+ new BasicNameValuePair("o", "booh"))));
+ }
+
+ @Test
+ public void testParseEscape() throws Exception {
+ Assert.assertThat(impl.parse("cn=blah\\, blah, ou=yada, o=booh"),
+ CoreMatchers.equalTo(Arrays.<NameValuePair>asList(
+ new BasicNameValuePair("cn", "blah, blah"),
+ new BasicNameValuePair("ou", "yada"),
+ new BasicNameValuePair("o", "booh"))));
+ }
+
+ @Test
+ public void testParseUnescapedEqual() throws Exception {
+ Assert.assertThat(impl.parse("c = cn=uuh, cn=blah, ou=yada, o=booh"),
+ CoreMatchers.equalTo(Arrays.<NameValuePair>asList(
+ new BasicNameValuePair("c", "cn=uuh"),
+ new BasicNameValuePair("cn", "blah"),
+ new BasicNameValuePair("ou", "yada"),
+ new BasicNameValuePair("o", "booh"))));
+ }
+
+ @Test
+ public void testParseInvalid() throws Exception {
+ Assert.assertThat(impl.parse("blah,blah"),
+ CoreMatchers.equalTo(Arrays.<NameValuePair>asList(
+ new BasicNameValuePair("blah", null),
+ new BasicNameValuePair("blah", null))));
+ }
+
+ @Test
+ public void testParseInvalid2() throws Exception {
+ Assert.assertThat(impl.parse("cn,o=blah"),
+ CoreMatchers.equalTo(Arrays.<NameValuePair>asList(
+ new BasicNameValuePair("cn", null),
+ new BasicNameValuePair("o", "blah"))));
+ }
+
+}