diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index b2a56e99c..2bedf4df1 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -2,6 +2,11 @@ Changes since release 4.3 BETA2 ------------------- +* [HTTPCLIENT-1383] HttpClient enters an infinite loop during NTLM authentication if the opposite + endpoint keeps responding with a type 2 NTLM response after type 3 MTLM message has already been + sent by the client. + Contributed by Oleg Kalnichevski + * [HTTPCLIENT-1372] Refactor HttpMultipart, and add RFC6532 mode, so that headers in post are no longer constrained to ASCII values. Contributed by Karl Wright diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java index 7c9957d98..06598c93c 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java @@ -99,17 +99,17 @@ public class NTLMScheme extends AuthSchemeBase { protected void parseChallenge( final CharArrayBuffer buffer, final int beginIndex, final int endIndex) throws MalformedChallengeException { - final String challenge = buffer.substringTrimmed(beginIndex, endIndex); - if (challenge.length() == 0) { + this.challenge = buffer.substringTrimmed(beginIndex, endIndex); + if (this.challenge.length() == 0) { if (this.state == State.UNINITIATED) { this.state = State.CHALLENGE_RECEIVED; } else { this.state = State.FAILED; } - this.challenge = null; } else { - this.state = State.MSG_TYPE2_RECEVIED; - this.challenge = challenge; + if (this.state == State.MSG_TYPE1_GENERATED) { + this.state = State.MSG_TYPE2_RECEVIED; + } } } diff --git a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientAuthenticationFakeNTLM.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientAuthenticationFakeNTLM.java new file mode 100644 index 000000000..032ba62ea --- /dev/null +++ b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientAuthenticationFakeNTLM.java @@ -0,0 +1,147 @@ +/* + * ==================================================================== + * 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 + * . + * + */ +package org.apache.http.impl.client.integration; + +import org.apache.http.HttpException; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.HttpVersion; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.NTCredentials; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.localserver.LocalTestServer; +import org.apache.http.message.BasicStatusLine; +import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpRequestHandler; +import org.apache.http.util.EntityUtils; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +/** + * Unit tests for some of the NTLM auth functionality.. + */ +public class TestClientAuthenticationFakeNTLM extends IntegrationTestBase { + + @Before + public void setUp() throws Exception { + this.localServer = new LocalTestServer(null, null); + } + + static class NtlmResponseHandler implements HttpRequestHandler { + + public void handle( + final HttpRequest request, + final HttpResponse response, + final HttpContext context) throws HttpException, IOException { + response.setStatusLine(new BasicStatusLine( + HttpVersion.HTTP_1_1, + HttpStatus.SC_UNAUTHORIZED, + "Authentication Required")); + response.setHeader("Connection", "Keep-Alive"); + response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM"); + } + } + + @Test + public void testNTLMAuthenticationFailure() throws Exception { + this.localServer.register("*", new NtlmResponseHandler()); + this.localServer.start(); + + final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials(AuthScope.ANY, + new NTCredentials("test", "test", "", "")); + + this.httpclient = HttpClients.custom() + .setDefaultCredentialsProvider(credsProvider) + .build(); + + final HttpContext context = HttpClientContext.create(); + + final HttpHost targethost = getServerHttp(); + final HttpGet httpget = new HttpGet("/"); + + final HttpResponse response = this.httpclient.execute(targethost, httpget, context); + EntityUtils.consume(response.getEntity()); + Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, + response.getStatusLine().getStatusCode()); + } + + static class NtlmType2ResponseHandler implements HttpRequestHandler { + + public void handle( + final HttpRequest request, + final HttpResponse response, + final HttpContext context) throws HttpException, IOException { + response.setStatusLine(new BasicStatusLine( + HttpVersion.HTTP_1_1, + HttpStatus.SC_UNAUTHORIZED, + "Authentication Required")); + response.setHeader("Connection", "Keep-Alive"); + if (!request.containsHeader(HttpHeaders.AUTHORIZATION)) { + response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM"); + } else { + response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM TlRMTVNTUAACAA" + + "AADAAMADgAAAAzwoICLgEjRWfCicKrw43DrwAAAAAAAAAAAAAAAAAAAAAGAHAX" + + "AAAAD1MAZQByAHYAZQByAA=="); + } + } + } + + @Test + public void testNTLMType2() throws Exception { + this.localServer.register("*", new NtlmType2ResponseHandler()); + this.localServer.start(); + + final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials(AuthScope.ANY, + new NTCredentials("test", "test", "", "")); + + this.httpclient = HttpClients.custom() + .setDefaultCredentialsProvider(credsProvider) + .build(); + + final HttpContext context = HttpClientContext.create(); + + final HttpHost targethost = getServerHttp(); + final HttpGet httpget = new HttpGet("/"); + + final HttpResponse response = this.httpclient.execute(targethost, httpget, context); + EntityUtils.consume(response.getEntity()); + Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, + response.getStatusLine().getStatusCode()); + } + +}